diff --git a/packages/tables/src/elements/Cell.spec.tsx b/packages/tables/src/elements/Cell.spec.tsx
index 8bcf2c6eaf3..c2566698e90 100644
--- a/packages/tables/src/elements/Cell.spec.tsx
+++ b/packages/tables/src/elements/Cell.spec.tsx
@@ -13,6 +13,8 @@ import { Table } from './Table';
import { Body } from './Body';
import { Row } from './Row';
import { Cell } from './Cell';
+import { Head } from './Head';
+import { HeaderRow } from './HeaderRow';
describe('Cell', () => {
it('passes ref to underlying DOM element', () => {
@@ -113,4 +115,18 @@ describe('Cell', () => {
expect(getByTestId('cell').firstChild).toHaveStyle(hideVisually());
});
+
+ it('renders dangerouslySetInnerHTML content', () => {
+ const { getByTestId } = render(
+
+ );
+
+ expect(getByTestId('cell').innerHTML).toBe('Name');
+ });
});
diff --git a/packages/tables/src/elements/Cell.tsx b/packages/tables/src/elements/Cell.tsx
index 42f6ce32943..e2f4d06cea2 100644
--- a/packages/tables/src/elements/Cell.tsx
+++ b/packages/tables/src/elements/Cell.tsx
@@ -9,6 +9,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { StyledCell, StyledHiddenCell } from '../styled';
import { useTableContext } from '../utils/useTableContext';
+import { HeaderRowContext } from '../utils/useHeaderRowContext';
import { ICellProps } from '../types';
/**
@@ -21,20 +22,23 @@ export const Cell = React.forwardRef(
const { size } = useTableContext();
return (
-
- {hidden && props.children ? (
- {props.children}
- ) : (
- props.children
- )}
-
+ // the cell's content (for example, a nested table) is not in a header row
+
+
+ {hidden && props.children ? (
+ {props.children}
+ ) : (
+ props.children
+ )}
+
+
);
}
);
diff --git a/packages/tables/src/elements/HeaderCell.spec.tsx b/packages/tables/src/elements/HeaderCell.spec.tsx
index 2a7d5d1af13..b9c96fbe772 100644
--- a/packages/tables/src/elements/HeaderCell.spec.tsx
+++ b/packages/tables/src/elements/HeaderCell.spec.tsx
@@ -13,6 +13,9 @@ import { Table } from './Table';
import { Head } from './Head';
import { HeaderRow } from './HeaderRow';
import { HeaderCell } from './HeaderCell';
+import { Body } from './Body';
+import { Row } from './Row';
+import { Cell } from './Cell';
describe('HeaderCell', () => {
it('passes ref to underlying DOM element', () => {
@@ -87,4 +90,136 @@ describe('HeaderCell', () => {
expect(getByTestId('headerCell').firstChild).toHaveStyle(hideVisually());
});
+
+ it('renders dangerouslySetInnerHTML content', () => {
+ const { getByTestId } = render(
+
+
+
+ Name' }}
+ />
+
+
+
+ );
+
+ expect(getByTestId('headerCell').innerHTML).toBe('Name');
+ });
+
+ describe('scope', () => {
+ it('defaults to column scope in a header row', () => {
+ const { getByTestId } = render(
+
+ );
+
+ expect(getByTestId('headerCell')).toHaveAttribute('scope', 'col');
+ });
+
+ it('keeps an explicit scope', () => {
+ const { getByTestId } = render(
+
+ );
+
+ expect(getByTestId('headerCell')).toHaveAttribute('scope', 'colgroup');
+ });
+
+ it('applies the default when the given scope is undefined', () => {
+ const { getByTestId } = render(
+
+ );
+
+ expect(getByTestId('headerCell')).toHaveAttribute('scope', 'col');
+ });
+
+ it('sets no scope in a body row, where the browser infers it', () => {
+ const { getByTestId } = render(
+
+
+
+ Name
+ Price
+
+
+ Apple
+ | 1 |
+
+
+ Pear
+ | 2 |
+
+
+
+ );
+
+ expect(getByTestId('columnHeader')).not.toHaveAttribute('scope');
+ expect(getByTestId('rowHeader')).not.toHaveAttribute('scope');
+ expect(getByTestId('plainRowHeader')).not.toHaveAttribute('scope');
+ });
+
+ it('does not apply the header row default inside cell content', () => {
+ const { getByTestId } = render(
+
+
+
+
+
+
+ |
+
+ |
+
+
+ |
+
+
+
+ );
+
+ expect(getByTestId('inHeaderCell')).not.toHaveAttribute('scope');
+ expect(getByTestId('inCell')).not.toHaveAttribute('scope');
+ expect(getByTestId('inNestedTable')).not.toHaveAttribute('scope');
+ });
+ });
});
diff --git a/packages/tables/src/elements/HeaderCell.tsx b/packages/tables/src/elements/HeaderCell.tsx
index 4a7cb703c25..2ab1a307c36 100644
--- a/packages/tables/src/elements/HeaderCell.tsx
+++ b/packages/tables/src/elements/HeaderCell.tsx
@@ -9,6 +9,7 @@ import React, { forwardRef } from 'react';
import { IHeaderCellProps } from '../types';
import { StyledHeaderCell, StyledHiddenCell } from '../styled';
import { useTableContext } from '../utils/useTableContext';
+import { HeaderRowContext, useHeaderRowContext } from '../utils/useHeaderRowContext';
import { Cell } from './Cell';
/**
@@ -17,24 +18,29 @@ import { Cell } from './Cell';
* @extends ThHTMLAttributes
*/
export const HeaderCell = forwardRef(
- ({ hidden, isMinimum, isTruncated, hasOverflow, ...props }, ref) => {
+ ({ hidden, isMinimum, isTruncated, hasOverflow, scope, ...props }, ref) => {
const { size } = useTableContext();
+ const isInHeaderRow = useHeaderRowContext();
return (
-
- {hidden && props.children ? (
- {props.children}
- ) : (
- props.children
- )}
-
+ // the cell's content (for example, a nested table) is not in a header row
+
+
+ {hidden && props.children ? (
+ {props.children}
+ ) : (
+ props.children
+ )}
+
+
);
}
);
diff --git a/packages/tables/src/elements/HeaderRow.tsx b/packages/tables/src/elements/HeaderRow.tsx
index a57f751d1a3..13923f4cd7a 100644
--- a/packages/tables/src/elements/HeaderRow.tsx
+++ b/packages/tables/src/elements/HeaderRow.tsx
@@ -8,6 +8,7 @@
import React, { HTMLAttributes } from 'react';
import { StyledHeaderRow } from '../styled';
import { useTableContext } from '../utils/useTableContext';
+import { HeaderRowContext } from '../utils/useHeaderRowContext';
/**
* @deprecated use `Table.HeaderRow` instead
@@ -18,7 +19,11 @@ export const HeaderRow = React.forwardRef {
const { size } = useTableContext();
- return ;
+ return (
+
+
+
+ );
}
);
diff --git a/packages/tables/src/elements/SortableCell.spec.tsx b/packages/tables/src/elements/SortableCell.spec.tsx
index c197cec06e1..e3183209006 100644
--- a/packages/tables/src/elements/SortableCell.spec.tsx
+++ b/packages/tables/src/elements/SortableCell.spec.tsx
@@ -13,6 +13,8 @@ import { Table } from './Table';
import { Head } from './Head';
import { HeaderRow } from './HeaderRow';
import { SortableCell } from './SortableCell';
+import { Body } from './Body';
+import { Row } from './Row';
import { StyledSortableButton } from '../styled';
describe('SortableCell', () => {
@@ -76,4 +78,50 @@ describe('SortableCell', () => {
` as any
});
});
+
+ describe('scope', () => {
+ it('defaults to column scope in a header row', () => {
+ const { getByTestId } = render(
+
+ );
+
+ expect(getByTestId('cell')).toHaveAttribute('scope', 'col');
+ });
+
+ it('sets no scope in a body row', () => {
+ const { getByTestId } = render(
+
+ );
+
+ expect(getByTestId('cell')).not.toHaveAttribute('scope');
+ });
+
+ it('keeps an explicit scope from cell props', () => {
+ const { getByTestId } = render(
+
+ );
+
+ expect(getByTestId('cell')).toHaveAttribute('scope', 'colgroup');
+ });
+ });
});
diff --git a/packages/tables/src/elements/SortableCell.tsx b/packages/tables/src/elements/SortableCell.tsx
index 3df6d10ce10..f72aad21aa2 100644
--- a/packages/tables/src/elements/SortableCell.tsx
+++ b/packages/tables/src/elements/SortableCell.tsx
@@ -16,6 +16,7 @@ import {
StyledSortableStrokeIconWrapper,
StyledSortableFillIconWrapper
} from '../styled';
+import { useHeaderRowContext } from '../utils/useHeaderRowContext';
/**
* @deprecated use `Table.SortableCell` instead
@@ -24,7 +25,8 @@ import {
*/
export const SortableCell = forwardRef(
({ sort, cellProps = {}, width, children, ...sortableButtonProps }, ref) => {
- const { isMinimum, isTruncated, hasOverflow, ...otherCellProps } = cellProps;
+ const { isMinimum, isTruncated, hasOverflow, scope, ...otherCellProps } = cellProps;
+ const isInHeaderRow = useHeaderRowContext();
let ariaSortValue = 'none';
if (sort === 'asc') {
@@ -38,6 +40,7 @@ export const SortableCell = forwardRef(
return (
(
return (
-
+ {/* a nested table starts outside any header row */}
+
+
+
);
}
diff --git a/packages/tables/src/utils/useHeaderRowContext.ts b/packages/tables/src/utils/useHeaderRowContext.ts
new file mode 100644
index 00000000000..d1a166e45f7
--- /dev/null
+++ b/packages/tables/src/utils/useHeaderRowContext.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright Zendesk, Inc.
+ *
+ * Use of this source code is governed under the Apache License, Version 2.0
+ * found at http://www.apache.org/licenses/LICENSE-2.0.
+ */
+
+import React, { useContext } from 'react';
+
+/** Whether a header cell sits directly in a `Table.HeaderRow` */
+export const HeaderRowContext = React.createContext(false);
+
+export const useHeaderRowContext = () => {
+ return useContext(HeaderRowContext);
+};