Skip to content

USWDS - Table: Add sticky headers #5420

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 44 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
9a8a13a
add the ability to sticky the header of the table component
etanb Dec 20, 2022
567876c
prettier
etanb Dec 20, 2022
93269e5
Merge branch 'develop' into eb-sticky-table-header
etanb Apr 17, 2023
630eadc
fixes to implementation
etanb Apr 18, 2023
4c9a177
Add sticky header control
May 16, 2023
ab1e642
Override position:relative on sortable sticky th cells
May 16, 2023
455296b
Remove stickyheader control
May 16, 2023
db6c5f7
Add sortable JSON file
May 16, 2023
3b03d79
Add demo for multi-row headers
May 16, 2023
8dae8db
Add multipleHeaderRows control for testing
May 17, 2023
a060c6a
Remove background color and extra borders from sticky table thead
May 17, 2023
37d56a8
Disable multipleHeaderRows for sortable tables
May 17, 2023
63f925b
Remove text-right from table header
May 17, 2023
08024a6
Merge branch 'develop' of https://github.com/uswds/uswds into al-stic…
May 19, 2023
81c43dd
Run prettier
May 19, 2023
40dac7c
Add settings for table background color and sticky table top offset
May 23, 2023
a477cd0
Move stickyheader from json to a storybook control
Aug 2, 2023
ab585ae
Remove stickyheader check on scrollable variant
Aug 2, 2023
da2a9e7
Format code
Aug 2, 2023
6bd2f54
Move scrollable setting from json to storybook
Aug 2, 2023
fd01eb4
Let $theme-table-background-color accept default
Aug 2, 2023
54debdc
Run prettier
Aug 2, 2023
121762b
Fix default for $theme-table-background-color
Aug 2, 2023
65c03b6
Clean up multiple header rows code
Aug 2, 2023
feaaecd
Add scrollable conditional to sortable twig
Aug 3, 2023
ba30313
Create loops for header and body rows
Aug 4, 2023
17b90e5
Remove background color on table button default
Aug 4, 2023
d02ebdc
Merge branch 'develop' of https://github.com/uswds/uswds into al-stic…
Aug 9, 2023
bef2f1e
Restore scrollable default on sortable tables
Aug 9, 2023
d3162cf
Clean up sticky header style rules
Aug 9, 2023
a9e17ac
Reorganize sticky header controls
Aug 9, 2023
7ca071b
Move scrollable: true to json file
Aug 11, 2023
d6c4ffb
Run prettier
Aug 11, 2023
21cec70
Add !default to $theme-table-sticky-top-offset
Aug 21, 2023
0d8a54d
Move 'stickyheader:true' to JSON
Sep 6, 2023
c809938
Merge branch 'develop' of github.com:uswds/uswds into al-sticky-table…
Jan 9, 2024
7ef482c
Add note about sticky header to scrollable control
Jan 10, 2024
02034a7
Use underscores for twig variables
Jan 10, 2024
58d0a6a
Add note to sticky header + mult header controls
Jan 11, 2024
c4d13e7
Add underscore to stickheader json + twig
Jan 11, 2024
b79fdf2
Remove "Note:" from scrollable control
Jan 11, 2024
1e447af
Create test: multiple header rows + sticky header
Jan 23, 2024
cae00e5
Assign sticky_header_class on sortable twig
Jan 23, 2024
b10cae4
Run prettier
Jan 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/usa-table/src/styles/_usa-table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
}
}

.usa-table--sticky-header {
@include usa-table--sticky-header;
}

.usa-table-container--scrollable {
@include usa-table-container--scrollable;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"scrollable": true
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the class, but not seeing sticky headers work in any of these tables.

Example
image
From preview Table - Sortable ⋅ Storybook

Copy link
Contributor Author

@amyleadem amyleadem Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to first turn off the scrollable variant in the control since it is incompatible with sticky headers. I added a note to the scrollable control in 58d0a6a to help clarify.

Let me know if that helps.

Copy link
Contributor

@mejiaj mejiaj Jan 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amyleadem is it worth making these controls conditional1? For example, only show these controls if sticky headers are disabled.

Example

  argTypes: {
    scrollable: {
      name: "Scrollable (Turning this on will disable sticky headers)",
      control: { type: "boolean" },
      defaultValue: false,
      if: { arg: "sticky_header", truthy: false } // Hide this control if sticky
    },
  },

Footnotes

  1. https://storybook.js.org/docs/essentials/controls#conditional-controls

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I played with this a bit but lean away from adding it in this instance since neither is actually the dependency of the other. Instead they are just incompatible. I ended up feeling like I would be confused about which options are available for the component, so I left it as is.

Let me know if you object.

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% if scrollable %}
<div class="usa-table-container--scrollable" tabindex="0">
<table class="usa-table">
{% endif %}
<table class="usa-table {{sticky_header ? 'usa-table--sticky-header' : ''}}">
<caption>Recently admitted US states (sortable table example)</caption>
<thead>
<tr>
Expand Down Expand Up @@ -87,10 +89,14 @@
<!-- this content will change when sort changes -->
</div>
<p class="margin-bottom-3">Data for illustration purposes only.</p>
{% if scrollable %}
</div>
{% endif %}

{% if scrollable %}
<div class="usa-table-container--scrollable" tabindex="0">
<table class="usa-table usa-table--striped">
{% endif %}
<table class="usa-table usa-table--striped {{sticky_header ? 'usa-table--sticky-header' : ''}}">
<caption>Sortable striped table with various content types</caption>
<thead>
<tr>
Expand Down Expand Up @@ -158,10 +164,14 @@
</tbody>
</table>
<div class="usa-sr-only usa-table__announcement-region" aria-live="polite"></div>
{% if scrollable %}
</div>
{% endif %}

{% if scrollable %}
<div class="usa-table-container--scrollable" tabindex="0">
<table class="usa-table usa-table--borderless">
{% endif %}
<table class="usa-table usa-table--borderless {{sticky_header ? 'usa-table--sticky-header' : ''}}">
<caption>Sortable borderless table with various content types</caption>
<thead>
<tr>
Expand Down Expand Up @@ -228,4 +238,6 @@
</tbody>
</table>
<div class="usa-sr-only usa-table__announcement-region" aria-live="polite"></div>
{% if scrollable %}
</div>
{% endif %}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"scrollable": false,
"modifier": "",
"caption": "Bordered table",
"thead": [
Expand Down
1 change: 0 additions & 1 deletion packages/usa-table/src/usa-table.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"scrollable": false,
"modifier": "",
"caption": "Bordered table",
"thead": [
Expand Down
33 changes: 33 additions & 0 deletions packages/usa-table/src/usa-table.stories.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import Component from "./usa-table.twig";
import SortableComponent from "./usa-table--sortable/usa-table--sortable.twig";
import SortableContent from "./usa-table--sortable/usa-table--sortable.json";
import DefaultContent from "./usa-table.json";
import BorderlessContent from "./usa-table~borderless.json";
import StripedContent from "./usa-table~striped.json";
import StickyHeaderContent from "./usa-table~stickyheader.json";

export default {
title: "Components/Table",
argTypes: {
scrollable: {
name: "Scrollable (Turning this on will disable sticky headers)",
control: { type: "boolean" },
defaultValue: false,
},
sticky_header: {
name: "Sticky header",
control: { type: "boolean" },
defaultValue: false,
},
multiple_header_rows: {
name: "Multiple header rows (For sticky header tests: Confirm that both thead rows are visible when sticky)",
control: { type: "boolean" },
defaultValue: false,
},
},
};

const Template = (args) => Component(args);
Expand All @@ -20,4 +39,18 @@ Borderless.args = BorderlessContent;
export const Striped = Template.bind({});
Striped.args = StripedContent;

export const StickyHeader = Template.bind({});
StickyHeader.args = {
...DefaultContent,
...StickyHeaderContent,
};

export const Sortable = SortableTemplate.bind({});
Sortable.args = {
...SortableContent,
};
Sortable.argTypes = {
multiple_header_rows: {
table: { disable: true },
},
};
46 changes: 31 additions & 15 deletions packages/usa-table/src/usa-table.twig
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
{% if multiple_header_rows %}
{% set header_loop_count = 0..1 %}
{% else %}
{% set header_loop_count = 0..0 %}
{% endif %}

{% set body_loop_count = 0..0 %}
{% if sticky_header %}
{% set body_loop_count = 0..6 %}
{% set sticky_header_class = 'usa-table--sticky-header' %}
{% endif %}

{% if scrollable %}
<div class="usa-table-container--scrollable" tabindex="0">
{% endif %}

<table class="usa-table {{ modifier }}">
<table class="usa-table {{sticky_header ? sticky_header_class }} {{ modifier }}">
{% if caption %}
<caption>{{ caption }}</caption>
{% endif %}
Expand All @@ -14,24 +26,28 @@
{% endif %}

<thead>
<tr>
{% for th in thead %}
<th scope="{{ th.scope|default('col') }}">{{ th.title }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in tbody %}
{% for i in header_loop_count %}
<tr>
{% for cell in row %}
{% if not cell.scope %}
<td>{{ cell.title }}</td>
{% else %}
<th scope="{{ cell.scope }}">{{ cell.title }}</th>
{% endif %}
{% for th in thead %}
<th scope="{{ th.scope|default('col') }}">{{ th.title }}</th>
{% endfor %}
</tr>
{% endfor %}
</thead>
<tbody>
{% for i in body_loop_count %}
{% for row in tbody %}
<tr>
{% for cell in row %}
{% if not cell.scope %}
<td>{{ cell.title }}</td>
{% else %}
<th scope="{{ cell.scope }}">{{ cell.title }}</th>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>

Expand Down
1 change: 0 additions & 1 deletion packages/usa-table/src/usa-table~borderless.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"scrollable": false,
"modifier": "usa-table--borderless",
"caption": "Borderless table: A borderless table can be useful when you want the information to feel more a part of the text it accompanies and extends.",
"thead": [
Expand Down
4 changes: 4 additions & 0 deletions packages/usa-table/src/usa-table~stickyheader.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"caption": "Sticky header table",
"sticky_header": true
}
1 change: 0 additions & 1 deletion packages/usa-table/src/usa-table~striped.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"scrollable": false,
"modifier": "usa-table--striped",
"caption": "Bordered table with horizontal stripes",
"thead": [
Expand Down
52 changes: 49 additions & 3 deletions packages/uswds-core/src/styles/mixins/general/table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
@use "../utilities" as *;
@use "../typography/typeset" as *;

$table-background-color: $theme-table-background-color;

@if $table-background-color == "default" {
$table-background-color: get-default("bg-color");
}
Comment on lines +10 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

In the future, we should consider adding default support for color().

Especially as we rely more on default for background color setting only.


$table-text-color: get-color-token-from-bg(
$bg-color: $theme-body-background-color,
$bg-color: $theme-table-background-color,
$preferred-text-token: $theme-table-text-color,
$context: "Table text",
);
Expand Down Expand Up @@ -235,7 +241,7 @@ $table-unsorted-icon-color: get-color-token-from-bg(
}
th,
td {
background-color: color($theme-body-background-color);
background-color: color($table-background-color);
border: 1px solid color($theme-table-border-color);
font-weight: fw("normal");
padding: units(1) units(2);
Expand Down Expand Up @@ -295,7 +301,7 @@ $table-unsorted-icon-color: get-color-token-from-bg(
@mixin usa-table--borderless {
thead {
th {
background-color: transparent;
background-color: color($table-background-color);
border-top: 0;
color: color($table-text-color);

Expand Down Expand Up @@ -355,6 +361,46 @@ $table-unsorted-icon-color: get-color-token-from-bg(
@include table-stacked-header-styles;
}

@mixin usa-table--sticky-header {
border: 1px solid color($theme-table-border-color);
border-collapse: separate;

td,
th {
border-left: none;
border-top: none;

&:last-child {
border-right: none;
}
}

tbody tr:last-child {
td,
th {
border-bottom: none;
}
}
Comment on lines +365 to +383
Copy link
Contributor Author

@amyleadem amyleadem Aug 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

To prevent an issue with content showing behind the sticky table headers on scroll, I added border-collapse: separate.

This caused the cell borders to essentially double up, which created a need to reconfigure how the borders were applied in this variant.


thead {
position: sticky;
top: $theme-table-sticky-top-offset;
Copy link
Contributor Author

@amyleadem amyleadem Aug 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Offset is useful for when there is another element that already sticks to the top of the page, like a sticky header navigation.

I suspect that most users will need to write custom rules if their sticky elements change at different breakpoints (For example, the sticky header navigation on uswds-site goes away in the mobile view). Do we want to keep this setting with the understanding that additional style overrides might often be necessary

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should keep it. Like most of our work, we provide the tools, but it's up to the users to implement them in a way that works for them.

Copy link
Contributor

@mejiaj mejiaj Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting this in the guidance would be useful. As well as recommending users limit the number of sticky/fixed items on the page.

For example, a sticky sidenav, header, and table header might be too much. For now a general note might be good enough.

}

&.usa-table--borderless {
border: none;

thead th {
background-color: color($table-background-color);
}

td,
th {
border-right: none;
}
}
}

@mixin usa-table-container--scrollable {
margin: units(2.5) 0;
overflow-y: hidden;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ $theme-summary-box-link-color: default !default;
$theme-summary-box-text-color: default !default;

// Table
$theme-table-background-color: default !default;
$theme-table-border-color: "ink" !default;
$theme-table-header-background-color: "base-lighter" !default;
$theme-table-header-text-color: default !default;
Expand All @@ -213,6 +214,7 @@ $theme-table-sorted-header-background-color: "accent-cool-light" !default;
$theme-table-sorted-background-color: "accent-cool-lighter" !default;
$theme-table-sorted-stripe-background-color: "blue-cool-10v" !default;
$theme-table-sorted-icon-color: default !default;
$theme-table-sticky-top-offset: -1px !default;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small concern here is this is similar to the current hero setting where it's not very flexible or re-usable.

Not a blocker for this work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the setting is fairly limited, but I am not sure if there is a way around it. I would imagine a lot of projects will need to write some custom style rules for the top offset.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amyleadem does this setting accept units too or only pixels? Do we have any guidance for this?

$theme-table-unsorted-icon-color: "base" !default;

// Tooltips
Expand Down