diff --git a/packages/core/components/DataTable/DataTable.tsx b/packages/core/components/DataTable/DataTable.tsx
index 245f8f055..ceac0f5d2 100644
--- a/packages/core/components/DataTable/DataTable.tsx
+++ b/packages/core/components/DataTable/DataTable.tsx
@@ -21,6 +21,7 @@ import { TableConfig } from './types/TableConfig'
import { Column } from '../../types/Column'
import { pivotData } from '../../helpers/pivotData'
import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
+import './data-table.css'
export type DataTableProps = {
applyLegendToRow?: Function
@@ -131,26 +132,27 @@ const DataTable = (props: DataTableProps) => {
}
const rawRows = Object.keys(runtimeData).filter(column => column != 'columns')
- const rows = isVertical
- ? rawRows.sort((a, b) => {
- let dataA
- let dataB
- if (config.type === 'map' && config.columns) {
- const sortByColName = config.columns[sortBy.column].name
- dataA = runtimeData[a][sortByColName]
- dataB = runtimeData[b][sortByColName]
- }
- if (config.type === 'chart' || config.type === 'dashboard') {
- dataA = runtimeData[a][sortBy.column]
- dataB = runtimeData[b][sortBy.column]
- }
- if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date-time') {
- dataA = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[a][config.xAxis.dataKey])
- dataB = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[b][config.xAxis.dataKey])
- }
- return dataA && dataB ? customSort(dataA, dataB, sortBy, config) : 0
- })
- : rawRows
+ const rows =
+ isVertical && sortBy.column
+ ? rawRows.sort((a, b) => {
+ let dataA
+ let dataB
+ if (config.type === 'map' && config.columns) {
+ const sortByColName = config.columns[sortBy.column].name
+ dataA = runtimeData[a][sortByColName]
+ dataB = runtimeData[b][sortByColName]
+ }
+ if (['chart', 'dashboard', 'table'].includes(config.type)) {
+ dataA = runtimeData[a][sortBy.column]
+ dataB = runtimeData[b][sortBy.column]
+ }
+ if (!dataA && !dataB && config.type === 'chart' && config.xAxis && config.xAxis.type === 'date-time') {
+ dataA = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[a][config.xAxis.dataKey])
+ dataB = timeParse(config.runtime.xAxis.dateParseFormat)(runtimeData[b][config.xAxis.dataKey])
+ }
+ return dataA && dataB ? customSort(dataA, dataB, sortBy, config) : 0
+ })
+ : rawRows
const limitHeight = {
maxHeight: config.table.limitHeight && `${config.table.height}px`,
@@ -283,7 +285,7 @@ const DataTable = (props: DataTableProps) => {
)
}
tableOptions={{
- className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}${
+ className: `table table-striped ${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}${
isVertical ? '' : ' horizontal'
}`,
'aria-live': 'assertive',
@@ -311,7 +313,7 @@ const DataTable = (props: DataTableProps) => {
End Date |
}
- tableOptions={{ className: 'region-table data-table' }}
+ tableOptions={{ className: 'table table-striped region-table data-table' }}
fontSize={config.fontSize}
/>
)}
@@ -329,7 +331,7 @@ const DataTable = (props: DataTableProps) => {
@@ -344,7 +346,7 @@ const DataTable = (props: DataTableProps) => {
stickyHeader
headContent={}
tableOptions={{
- className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}`,
+ className: `table table-striped ${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}`,
'aria-live': 'assertive',
'aria-rowcount': 11,
hidden: !expanded
diff --git a/packages/core/components/DataTable/components/ChartHeader.tsx b/packages/core/components/DataTable/components/ChartHeader.tsx
index 2b1b8833d..190a389af 100644
--- a/packages/core/components/DataTable/components/ChartHeader.tsx
+++ b/packages/core/components/DataTable/components/ChartHeader.tsx
@@ -1,8 +1,9 @@
import { getChartCellValue } from '../helpers/getChartCellValue'
import { getSeriesName } from '../helpers/getSeriesName'
import { getDataSeriesColumns } from '../helpers/getDataSeriesColumns'
-import { DownIcon, UpIcon } from './Icons'
import ScreenReaderText from '@cdc/core/components/elements/ScreenReaderText'
+import { SortIcon } from './SortIcon'
+import { getNewSortBy } from '../helpers/getNewSortBy'
type ChartHeaderProps = { data; isVertical; config; setSortBy; sortBy; hasRowType? }
@@ -19,17 +20,6 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, hasRowType }
}
}
- const handleHeaderClasses = (sortBy, text) => {
- let classes = ['sort']
- if (sortBy.column === text && sortBy.asc) {
- classes.push('sort-asc')
- }
- if (sortBy.column === text && sortBy.desc) {
- classes.push('sort-desc')
- }
- return classes.join(' ')
- }
-
const ScreenReaderSortByText = ({ text, config, sortBy }) => {
const notApplicableText = 'Not Applicable'
let columnHeaderText = `${text} `
@@ -47,13 +37,18 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, hasRowType }
if (columnHeaderText === notApplicableText) return
- return {`Press command, modifier, or enter key to sort by ${columnHeaderText} in ${sortBy.column !== columnHeaderText ? 'ascending' : sortBy.column === 'desc' ? 'descending' : 'ascending'} order`}
+ return (
+ {`Press command, modifier, or enter key to sort by ${columnHeaderText} in ${
+ sortBy.column !== columnHeaderText ? 'ascending' : sortBy.column === 'desc' ? 'descending' : 'ascending'
+ } order`}
+ )
}
- const ColumnHeadingText = ({ column, text, config }) => {
+ const ColumnHeadingText = ({ text, config }) => {
let notApplicableText = 'Not Applicable'
if (text === '__series__' && config.table.indexLabel) return `${config.table.indexLabel} `
- if (text === '__series__' && !config.table.indexLabel) return {notApplicableText}
+ if (text === '__series__' && !config.table.indexLabel)
+ return {notApplicableText}
return text
}
@@ -71,7 +66,8 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, hasRowType }
{dataSeriesColumns.map((column, index) => {
const text = getSeriesName(column, config)
-
+ const newSortBy = getNewSortBy(sortBy, column, index)
+ const sortByAsc = sortBy.column === column ? sortBy.asc : undefined
return (
{
if (hasRowType) return
- setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false, colIndex: index })
+ setSortBy(newSortBy)
}}
onKeyDown={e => {
if (hasRowType) return
if (e.keyCode === 13) {
- setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false, colIndex: index })
+ setSortBy(newSortBy)
}
}}
- className={handleHeaderClasses(sortBy, text)}
- {...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
+ {...(sortBy.column === column
+ ? sortBy.asc
+ ? { 'aria-sort': 'ascending' }
+ : { 'aria-sort': 'descending' }
+ : null)}
>
-
- {column === sortBy.column && {!sortBy.asc ? : }}
+
+ {!hasRowType && }
|
)
@@ -107,7 +106,8 @@ const ChartHeader = ({ data, isVertical, config, setSortBy, sortBy, hasRowType }
{['__series__', ...Object.keys(data)].slice(sliceVal).map((row, index) => {
let column = config.xAxis?.dataKey
let text = row !== '__series__' ? getChartCellValue(row, column, config, data) : '__series__'
-
+ const newSortBy = getNewSortBy(text, index)
+ const sortByAsc = sortBy.colIndex === index ? sortBy.asc : undefined
return (
{
- setSortBy({ column: text, asc: sortBy.column === text ? !sortBy.asc : false, colIndex: index })
+ setSortBy(newSortBy)
}}
onKeyDown={e => {
if (e.keyCode === 13) {
- setSortBy({ column: text, asc: sortBy.column === text ? !sortBy.asc : false, colIndex: index })
+ setSortBy(newSortBy)
}
}}
- className={handleHeaderClasses(sortBy, text)}
- {...(sortBy.column === text ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
+ {...(sortBy.column === text
+ ? sortBy.asc
+ ? { 'aria-sort': 'ascending' }
+ : { 'aria-sort': 'descending' }
+ : null)}
>
-
- {index === sortBy.colIndex && {!sortBy.asc ? : }}
+
+ {!hasRowType && }
+
|
)
diff --git a/packages/core/components/DataTable/components/Icons.tsx b/packages/core/components/DataTable/components/Icons.tsx
deleted file mode 100644
index bf14636ec..000000000
--- a/packages/core/components/DataTable/components/Icons.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-export const UpIcon = () => (
-
-)
-export const DownIcon = () => (
-
-)
diff --git a/packages/core/components/DataTable/components/MapHeader.tsx b/packages/core/components/DataTable/components/MapHeader.tsx
index 6ee9f0b56..723018d1e 100644
--- a/packages/core/components/DataTable/components/MapHeader.tsx
+++ b/packages/core/components/DataTable/components/MapHeader.tsx
@@ -1,16 +1,18 @@
import { DataTableProps } from '../DataTable'
-import { DownIcon, UpIcon } from './Icons'
import ScreenReaderText from '../../elements/ScreenReaderText'
+import { SortIcon } from './SortIcon'
+import { getNewSortBy } from '../helpers/getNewSortBy'
type MapHeaderProps = DataTableProps & {
sortBy: { column; asc }
setSortBy: Function
}
-const ColumnHeadingText = ({ column, text, config }) => {
+const ColumnHeadingText = ({ text, config }) => {
let notApplicableText = 'Not Applicable'
if (text === '__series__' && config.table.indexLabel) return `${config.table.indexLabel} `
- if (text === '__series__' && !config.table.indexLabel) return {notApplicableText}
+ if (text === '__series__' && !config.table.indexLabel)
+ return {notApplicableText}
return text
}
@@ -21,7 +23,7 @@ const MapHeader = ({ columns, config, indexTitle, sortBy, setSortBy }: MapHeader
.filter(column => columns[column].dataTable === true && columns[column].name)
.map((column, index) => {
let text
- if (column !== 'geo') {
+ if (column && column !== 'geo') {
text = columns[column].label ? columns[column].label : columns[column].name
} else {
text = config.type === 'map' ? indexTitle : config.xAxis?.dataKey
@@ -29,7 +31,8 @@ const MapHeader = ({ columns, config, indexTitle, sortBy, setSortBy }: MapHeader
if (config.type === 'map' && (text === undefined || text === '')) {
text = 'Location'
}
-
+ const newSortBy = getNewSortBy(sortBy, column, index)
+ const sortByAsc = sortBy.column === column ? sortBy.asc : undefined
return (
{
- setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false })
+ setSortBy(newSortBy)
}}
onKeyDown={e => {
if (e.keyCode === 13) {
- setSortBy({ column, asc: sortBy.column === column ? !sortBy.asc : false })
+ setSortBy(newSortBy)
}
}}
className={sortBy.column === column ? (sortBy.asc ? 'sort sort-asc' : 'sort sort-desc') : 'sort'}
- {...(sortBy.column === column ? (sortBy.asc ? { 'aria-sort': 'ascending' } : { 'aria-sort': 'descending' }) : null)}
+ {...(sortBy.column === column
+ ? sortBy.asc
+ ? { 'aria-sort': 'ascending' }
+ : { 'aria-sort': 'descending' }
+ : null)}
>
- {sortBy.column === column && {!sortBy.asc ? : }}
- {`Sort by ${text} in ${sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'} order`}
+
+ {`Sort by ${text} in ${
+ sortBy.column === column ? (!sortBy.asc ? 'descending' : 'ascending') : 'descending'
+ } order`}
|
)
})}
diff --git a/packages/core/components/DataTable/components/SortIcon/index.tsx b/packages/core/components/DataTable/components/SortIcon/index.tsx
new file mode 100644
index 000000000..780bb3370
--- /dev/null
+++ b/packages/core/components/DataTable/components/SortIcon/index.tsx
@@ -0,0 +1,25 @@
+import './sort-icon.css'
+
+const UpIcon = ({ active }) => (
+
+)
+const DownIcon = ({ active }) => (
+
+)
+
+type SortIconProps = {
+ ascending?: boolean
+}
+
+export const SortIcon: React.FC = ({ ascending }) => {
+ return (
+
+
+
+
+ )
+}
diff --git a/packages/core/components/DataTable/components/SortIcon/sort-icon.css b/packages/core/components/DataTable/components/SortIcon/sort-icon.css
new file mode 100644
index 000000000..dcc7a978a
--- /dev/null
+++ b/packages/core/components/DataTable/components/SortIcon/sort-icon.css
@@ -0,0 +1,21 @@
+/* format the white triangle sort icon in data table headers */
+.sort-icon {
+ fill: white;
+ width: 15px;
+ height: 20px;
+ float: right;
+ position: relative;
+ :is(svg) {
+ position: absolute;
+ fill: rgba(255, 255, 255, 0.5);
+ &.active {
+ fill: white;
+ }
+ }
+ .up {
+ top: 0;
+ }
+ .down {
+ bottom: 0;
+ }
+}
diff --git a/packages/core/styles/_data-table.scss b/packages/core/components/DataTable/data-table.css
similarity index 74%
rename from packages/core/styles/_data-table.scss
rename to packages/core/components/DataTable/data-table.css
index ed8268eb7..7f3acc998 100644
--- a/packages/core/styles/_data-table.scss
+++ b/packages/core/components/DataTable/data-table.css
@@ -1,298 +1,286 @@
-.collapsed + .table-container {
- border-bottom: none;
-}
-.table-container {
- overflow-x: auto;
- border-right: 1px solid $lightGray;
- border-left: 1px solid $lightGray;
- border-bottom: 1px solid $lightGray;
-}
-
-div.data-table-heading {
- position: relative;
- background: rgba(0, 0, 0, 0.05);
- padding: 0.5em 0.7em;
- border: $lightGray 1px solid;
- cursor: pointer;
-
- svg {
- position: absolute;
- height: 100%;
- width: 15px;
- top: 0;
- right: 1em;
- }
-
- &:focus {
- z-index: 2;
- position: relative;
- }
- @include breakpoint(xs) {
- font-size: $font-small + 0.2em;
- }
-}
-
-table.horizontal {
- th,
- td {
- min-width: 200px;
- }
-}
-
-table.data-table {
- min-width: 100%;
- background: #fff;
- position: relative;
- border: none;
- overflow-x: auto;
- border-collapse: collapse;
- overflow: auto;
- appearance: none;
- table-layout: fixed;
- * {
- box-sizing: border-box;
- }
-
- thead {
- user-select: none;
- -moz-user-select: none;
- user-select: none;
-
- button {
- background: none;
- font-size: initial;
- color: #fff;
- border: 0;
- }
-
- tr {
- background: none;
- }
- }
- thead {
- color: #fff;
- background-color: $mediumGray;
- .resizer {
- cursor: e-resize;
- width: 10px;
- position: absolute;
- top: 0;
- bottom: 0;
- right: 0;
- touch-action: none;
- }
- tr {
- text-align: left;
- }
- th,
- td {
- padding: 0.5em 1.3em 0.5em 0.7em;
- line-height: normal;
- position: relative;
- text-align: left;
- cursor: pointer;
- border-right: 1px solid $lightGray !important;
- svg {
- margin-left: 1rem;
- }
- }
-
- th.sort {
- background-color: darken($mediumGray, 10%);
- background-repeat: no-repeat;
- background-position: right 0.5em center;
- background-size: 10px 5px;
- }
-
- // format the white triangle sort icon in data table headers
- .sort-icon {
- fill: white;
- width: 30px;
- float: right;
- align-self: center;
- position: absolute;
- right: 10px;
- top: 50%;
- transform: translateY(-50%);
- }
-
- th:last-child,
- td:last-child {
- border-right: 0;
- }
- }
-
- tbody {
- tr {
- width: 100%;
- &:hover {
- background: rgba(0, 0, 0, 0.05);
- }
- }
- }
-
- tr {
- border-bottom: solid 1px #e5e5e5;
- min-width: 100%; // Needed to fill content up
- &:last-child {
- border-bottom: 0;
- }
- }
-
- td {
- padding: 0.3em 0.7em;
-
- border-right: 1px solid rgba(0, 0, 0, 0.1);
- }
-
- th,
- td {
- width: 1% !important;
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
- &:last-child {
- border-right: 0 !important;
- }
-
- @include breakpoint(xs) {
- font-size: $font-small;
- }
- }
- tr {
- & > * {
- width: 100%;
- }
- }
- th {
- flex-grow: 1;
- }
-
- td {
- position: relative;
- flex-grow: 1;
- }
-
- td a {
- padding: 0.3em 0.7em;
- position: absolute;
- top: 0;
- bottom: 0;
- right: 0;
- left: 0;
- display: block;
- color: inherit;
- text-decoration: none;
- }
-
- td span.table-link {
- text-decoration: underline;
- cursor: pointer;
- color: #075290;
- svg {
- max-width: 13px;
- vertical-align: baseline;
- margin-left: 5px;
- }
- }
-
- .boxplot-td {
- //display: inline-block;
- //box-sizing: border-box;
- table-layout: fixed;
- width: 200;
- //min-width: 150px;
- //max-width: 400px;
- }
-}
-
-.no-data {
- position: relative;
- .no-data-message {
- @include emptyState;
- h3 {
- font-size: 1.3rem;
- font-weight: 600;
- margin-bottom: 0.3rem;
- }
- }
- tr:hover {
- background: #fff;
- }
- th,
- td {
- width: 50%;
- &::before {
- content: '\00a0';
- }
- }
-}
-
-.data-table-pagination {
- margin: 1rem 0;
- display: flex;
- align-items: center;
- ul {
- list-style: none;
- margin: 0 1rem 0 0;
- display: flex;
- li + li {
- margin-left: 0.3rem;
- }
- button {
- background: $mediumGray;
- padding: 0.6rem 0.8rem;
- &:hover {
- background: lighten($mediumGray, 5%);
- }
- }
- button.btn-next {
- &::before {
- content: ' ';
- background-image: url(../assets/icon-caret-filled-up.svg);
- background-size: 10px 5px;
- width: 10px;
- height: 5px;
- display: block;
- transform: rotate(90deg);
- }
- }
- button.btn-prev {
- &::before {
- content: ' ';
- background-image: url(../assets/icon-caret-filled-up.svg);
- background-size: 10px 5px;
- width: 10px;
- height: 5px;
- display: block;
- transform: rotate(-90deg);
- }
- }
- button[disabled] {
- background: $mediumGray;
- opacity: 0.3;
- cursor: default;
- &:hover {
- background: $mediumGray;
- }
- }
- }
-}
-
-.btn-download {
- color: #fff;
- float: right;
- text-decoration: none;
- transition: 0.3s all;
- margin: 1em 0;
- &:hover {
- transition: 0.3s all;
- }
-}
-.cove,
-.cdc-open-viz-module {
- .download-links a:not(:last-child) {
- margin-right: 10px;
- display: inline-block;
- }
-}
+.collapsed + .table-container {
+ border-bottom: none;
+}
+.table-container {
+ overflow-x: auto;
+ border-right: 1px solid var(--lightGray);
+ border-left: 1px solid var(--lightGray);
+ border-bottom: 1px solid var(--lightGray);
+}
+
+div.data-table-heading {
+ position: relative;
+ background: rgba(0, 0, 0, 0.05);
+ padding: 0.5em 0.7em;
+ border: var(--lightGray) 1px solid;
+
+ svg {
+ position: absolute;
+ height: 100%;
+ width: 15px;
+ top: 0;
+ right: 1em;
+ }
+
+ &:focus {
+ z-index: 2;
+ position: relative;
+ }
+ @media (max-width: var(--breakpoint-sm)) {
+ font-size: var(--font-small) + 0.2em;
+ }
+}
+
+table.horizontal {
+ th,
+ td {
+ min-width: 200px;
+ }
+}
+
+table.data-table {
+ margin-bottom: 0;
+ min-width: 100%;
+ background: #fff;
+ position: relative;
+ border: none;
+ overflow-x: auto;
+ border-collapse: collapse;
+ overflow: auto;
+ appearance: none;
+ table-layout: fixed;
+ * {
+ box-sizing: border-box;
+ }
+
+ thead {
+ user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+
+ button {
+ background: none;
+ font-size: initial;
+ color: #fff;
+ border: 0;
+ }
+
+ tr {
+ background: none;
+ }
+ }
+ thead {
+ color: #fff;
+ .resizer {
+ cursor: e-resize;
+ width: 10px;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ touch-action: none;
+ }
+ tr {
+ text-align: left;
+ }
+ th,
+ td {
+ padding: 0.5em 1.3em 0.5em 0.7em;
+ line-height: normal;
+ position: relative;
+ text-align: left;
+ border-right: 1px solid var(--lightGray) !important;
+ }
+
+ th {
+ background-color: var(--primary);
+ background-repeat: no-repeat;
+ background-position: right 0.5em center;
+ background-size: 10px 5px;
+ }
+
+ th:last-child,
+ td:last-child {
+ border-right: 0;
+ }
+ }
+
+ tbody {
+ tr {
+ width: 100%;
+ }
+ }
+
+ tr {
+ &.row-group {
+ background-color: var(--tertiary) !important;
+ font-weight: bold;
+ }
+ border-bottom: solid 1px #e5e5e5;
+ min-width: 100%; /* Needed to fill content up*/
+ &:last-child {
+ border-bottom: 0;
+ }
+ }
+
+ th,
+ td {
+ padding: 0.3em 0.7em;
+ border-right: 1px solid rgba(0, 0, 0, 0.1);
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ &:last-child {
+ border-right: 0 !important;
+ }
+
+ @media (max-width: var(--breakpoint-sm)) {
+ font-size: var(--font-small) + 0.2em;
+ }
+ }
+ tr {
+ & > * {
+ width: 100%;
+ }
+ }
+ th {
+ flex-grow: 1;
+ }
+
+ td {
+ position: relative;
+ flex-grow: 1;
+ svg {
+ margin-left: 1rem;
+ }
+ }
+
+ td a {
+ padding: 0.3em 0.7em;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ left: 0;
+ display: block;
+ color: inherit;
+ text-decoration: none;
+ }
+
+ td span.table-link {
+ text-decoration: underline;
+ cursor: pointer;
+ color: #075290;
+ svg {
+ max-width: 13px;
+ vertical-align: baseline;
+ margin-left: 5px;
+ }
+ }
+
+ .boxplot-td {
+ table-layout: fixed;
+ width: 200;
+ }
+}
+
+.no-data {
+ position: relative;
+ .no-data-message {
+ background: rgba(255, 255, 255, 0.5);
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ position: absolute;
+ text-align: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 7;
+ :is(h3) {
+ font-size: 1.3rem;
+ font-weight: 600;
+ margin-bottom: 0.3rem;
+ }
+ }
+ tr:hover {
+ background: #fff;
+ }
+ th,
+ td {
+ width: 50%;
+ &::before {
+ content: '\00a0';
+ }
+ }
+}
+
+.data-table-pagination {
+ margin: 1rem 0;
+ display: flex;
+ align-items: center;
+ ul {
+ list-style: none;
+ margin: 0 1rem 0 0;
+ display: flex;
+ li + li {
+ margin-left: 0.3rem;
+ }
+ button {
+ background: var(--mediumGray);
+ padding: 0.6rem 0.8rem;
+ &:hover {
+ background: lighten(var(--mediumGray), 5%);
+ }
+ }
+ button.btn-next {
+ &::before {
+ content: ' ';
+ background-image: url(../assets/icon-caret-filled-up.svg);
+ background-size: 10px 5px;
+ width: 10px;
+ height: 5px;
+ display: block;
+ transform: rotate(90deg);
+ }
+ }
+ button.btn-prev {
+ &::before {
+ content: ' ';
+ background-image: url(../assets/icon-caret-filled-up.svg);
+ background-size: 10px 5px;
+ width: 10px;
+ height: 5px;
+ display: block;
+ transform: rotate(-90deg);
+ }
+ }
+ button[disabled] {
+ background: var(--mediumGray);
+ opacity: 0.3;
+ cursor: default;
+ &:hover {
+ background: var(--mediumGray);
+ }
+ }
+ }
+}
+
+.btn-download {
+ color: #fff;
+ float: right;
+ text-decoration: none;
+ transition: 0.3s all;
+ margin: 1em 0;
+ &:hover {
+ transition: 0.3s all;
+ }
+}
+.cove,
+.cdc-open-viz-module {
+ .download-links a:not(:last-child) {
+ margin-right: 10px;
+ display: inline-block;
+ }
+}
diff --git a/packages/core/components/DataTable/helpers/customSort.ts b/packages/core/components/DataTable/helpers/customSort.ts
index 6cd503eea..35bf4e2b2 100644
--- a/packages/core/components/DataTable/helpers/customSort.ts
+++ b/packages/core/components/DataTable/helpers/customSort.ts
@@ -14,7 +14,7 @@ export const customSort = (a, b, sortBy, config) => {
const numberB = parseInt(b.match(/\d+$/)[0], 10)
// Compare the numeric parts
- return !sortBy.asc ? Number(numberA) - Number(numberB) : Number(numberB) - Number(numberA)
+ return sortBy.asc ? Number(numberA) - Number(numberB) : Number(numberB) - Number(numberA)
}
}
@@ -26,39 +26,35 @@ export const customSort = (a, b, sortBy, config) => {
const trimmedB = String(valueB).trim()
if (config.xAxis?.dataKey === sortBy.column && config.xAxis.type === 'date') {
- let dateA = parseDate(config.xAxis.dateParseFormat, trimmedA)
+ const dateA = parseDate(config.xAxis.dateParseFormat, trimmedA)?.getTime()
- let dateB = parseDate(config.xAxis.dateParseFormat, trimmedB)
-
- if (dateA && dateA.getTime) dateA = dateA.getTime()
-
- if (dateB && dateB.getTime) dateB = dateB.getTime()
-
- return !sortBy.asc ? dateA - dateB : dateB - dateA
+ const dateB = parseDate(config.xAxis.dateParseFormat, trimmedB)?.getTime()
+ console.log(dateA, dateB)
+ return sortBy.asc ? dateA - dateB : dateB - dateA
}
// Check if values are numbers
const isNumA = !isNaN(Number(valueA)) && valueA !== undefined && valueA !== null && trimmedA !== ''
const isNumB = !isNaN(Number(valueB)) && valueB !== undefined && valueB !== null && trimmedB !== ''
// Handle empty strings or spaces
- if (trimmedA === '' && trimmedB !== '') return !sortBy.asc ? -1 : 1
- if (trimmedA !== '' && trimmedB === '') return !sortBy.asc ? 1 : -1
+ if (trimmedA === '' && trimmedB !== '') return sortBy.asc ? -1 : 1
+ if (trimmedA !== '' && trimmedB === '') return sortBy.asc ? 1 : -1
// Both are numbers: Compare numerically
if (isNumA && isNumB) {
- return !sortBy.asc ? Number(valueA) - Number(valueB) : Number(valueB) - Number(valueA)
+ return sortBy.asc ? Number(valueA) - Number(valueB) : Number(valueB) - Number(valueA)
}
// Only A is a number
if (isNumA) {
- return !sortBy.asc ? -1 : 1
+ return sortBy.asc ? -1 : 1
}
// Only B is a number
if (isNumB) {
- return !sortBy.asc ? 1 : -1
+ return sortBy.asc ? 1 : -1
}
// Neither are numbers: Compare as strings
- return !sortBy.asc ? trimmedA.localeCompare(trimmedB) : trimmedB.localeCompare(trimmedA)
+ return sortBy.asc ? trimmedA.localeCompare(trimmedB) : trimmedB.localeCompare(trimmedA)
}
diff --git a/packages/core/components/DataTable/helpers/getNewSortBy.ts b/packages/core/components/DataTable/helpers/getNewSortBy.ts
new file mode 100644
index 000000000..b42a1af91
--- /dev/null
+++ b/packages/core/components/DataTable/helpers/getNewSortBy.ts
@@ -0,0 +1,26 @@
+export const getNewSortBy = (sortBy, column, index) => {
+ let asc
+ let sortByCol = column
+ const ascending = sortBy.asc === true
+ const descending = sortBy.asc === false
+ const isInactive = sortBy.asc === undefined
+ const noColumnSelected = sortBy.column === undefined
+ if (noColumnSelected || sortBy.column !== column) {
+ // this is the first time a column is clicked
+ asc = true
+ } else {
+ // clicking the same column
+ if (descending) {
+ // reset
+ sortByCol = undefined
+ asc = undefined
+ }
+ if (isInactive) {
+ asc = true
+ }
+ if (ascending) {
+ asc = false
+ }
+ }
+ return { column: sortByCol, asc, colIndex: index }
+}
diff --git a/packages/core/components/DataTable/helpers/tests/customSort.test.ts b/packages/core/components/DataTable/helpers/tests/customSort.test.ts
new file mode 100644
index 000000000..d44e7cfa0
--- /dev/null
+++ b/packages/core/components/DataTable/helpers/tests/customSort.test.ts
@@ -0,0 +1,52 @@
+import { describe, it, expect } from 'vitest'
+import { customSort } from '../customSort'
+
+describe('customSort()', () => {
+ it('should return positive number when a > b', () => {
+ const a = 3
+ const b = 1
+ const sortBy = { column: 'someColumn', asc: true, colIndex: 0 }
+ const config = { type: 'map' }
+ expect(customSort(a, b, sortBy, config)).greaterThan(0)
+ expect(customSort(a, b, sortBy, { type: 'notMap' })).greaterThan(0)
+ })
+ it('should return negative number when a < b', () => {
+ const a = 1
+ const b = 3
+ const sortBy = { column: 'someColumn', asc: true, colIndex: 0 }
+ const config = { type: 'map' }
+ expect(customSort(a, b, sortBy, config)).lessThan(0)
+ expect(customSort(a, b, sortBy, { type: 'notMap' })).lessThan(0)
+ })
+ it('works for dates', () => {
+ const a = 2000
+ const b = 1999
+ const sortBy = { column: 'someColumn', asc: true, colIndex: 0 }
+ expect(
+ customSort(a, b, sortBy, { xAxis: { dataKey: sortBy.column, dateParseFormat: '%Y', type: 'date' } })
+ ).greaterThan(0)
+ expect(
+ customSort(b, a, sortBy, { xAxis: { dataKey: sortBy.column, dateParseFormat: '%Y', type: 'date' } })
+ ).lessThan(0)
+ })
+ it('works for strings', () => {
+ const a = 'banana'
+ const b = 'apple'
+ const sortBy = { column: 'someColumn', asc: true, colIndex: 0 }
+ const config = { type: 'map' }
+ expect(customSort(a, b, sortBy, config)).greaterThan(0)
+ expect(customSort(a, b, sortBy, { type: 'notMap' })).greaterThan(0)
+ expect(customSort(b, a, sortBy, config)).lessThan(0)
+ expect(customSort(b, a, sortBy, { type: 'notMap' })).lessThan(0)
+ })
+ it('works for strings after number', () => {
+ const a = 'banana'
+ const b = '1'
+ const sortBy = { column: 'someColumn', asc: true, colIndex: 0 }
+ const config = { type: 'map' }
+ expect(customSort(a, b, sortBy, config)).greaterThan(0)
+ expect(customSort(a, b, sortBy, { type: 'notMap' })).greaterThan(0)
+ expect(customSort(b, a, sortBy, config)).lessThan(0)
+ expect(customSort(b, a, sortBy, { type: 'notMap' })).lessThan(0)
+ })
+})
diff --git a/packages/core/components/DataTable/helpers/tests/getNewSortBy.test.ts b/packages/core/components/DataTable/helpers/tests/getNewSortBy.test.ts
new file mode 100644
index 000000000..c06c64862
--- /dev/null
+++ b/packages/core/components/DataTable/helpers/tests/getNewSortBy.test.ts
@@ -0,0 +1,26 @@
+import { describe, it, expect } from 'vitest'
+import { getNewSortBy } from '../getNewSortBy'
+
+describe('getNewSortBy()', () => {
+ it('should return ascending when currently undefined', () => {
+ const sortBy = { column: undefined, asc: undefined, colIndex: 0 }
+ const column = 'someColumn'
+ const index = 1
+ const result = getNewSortBy(sortBy, column, index)
+ expect(result).toEqual({ column, asc: true, colIndex: index })
+ })
+ it('should return ascending false when currently true', () => {
+ const sortBy = { column: 'someColumn', asc: true, colIndex: 0 }
+ const column = 'someColumn'
+ const index = 1
+ const result = getNewSortBy(sortBy, column, index)
+ expect(result).toEqual({ column, asc: false, colIndex: index })
+ })
+ it('should return ascending undefined when currently false', () => {
+ const sortBy = { column: 'someColumn', asc: false, colIndex: 0 }
+ const column = 'someColumn'
+ const index = 1
+ const result = getNewSortBy(sortBy, column, index)
+ expect(result).toEqual({ column: undefined, asc: undefined, colIndex: index })
+ })
+})
diff --git a/packages/core/components/Table/components/GroupRow.tsx b/packages/core/components/Table/components/GroupRow.tsx
index eea9004b9..aa59ad898 100644
--- a/packages/core/components/Table/components/GroupRow.tsx
+++ b/packages/core/components/Table/components/GroupRow.tsx
@@ -8,7 +8,7 @@ type GroupRowProps = {
const GroupRow = ({ label, colSpan, data }: GroupRowProps) => {
return (
-
+
{label}
|
diff --git a/packages/core/components/_stories/styles.scss b/packages/core/components/_stories/styles.scss
index 5a75c69b4..54e7d4af6 100644
--- a/packages/core/components/_stories/styles.scss
+++ b/packages/core/components/_stories/styles.scss
@@ -1,7 +1,6 @@
@import '../../styles/base.scss';
@import '../../styles/_variables';
@import '../../styles/_mixins';
-@import '../../styles/_data-table';
@import '../../styles/_global.scss';
.visually-hidden {
diff --git a/packages/core/styles/_global-variables.scss b/packages/core/styles/_global-variables.scss
index bcfed7754..dd6dd3166 100644
--- a/packages/core/styles/_global-variables.scss
+++ b/packages/core/styles/_global-variables.scss
@@ -70,6 +70,15 @@ $colors: (
--#{$key}: #{$value};
}
--editorWidth: 350px;
+
+ --font-small: 0.7em;
+
+ --breakpoint-xs: 480px;
+ --breakpoint-sm: 768px;
+ --breakpoint-md: 960px;
+ --breakpoint-lg: 1170px;
+ --breakpoint-xl: 1280px;
+
}
}
diff --git a/packages/core/styles/_reset.scss b/packages/core/styles/_reset.scss
index d3280a9af..049c6ba74 100644
--- a/packages/core/styles/_reset.scss
+++ b/packages/core/styles/_reset.scss
@@ -18,7 +18,6 @@
}
}
.cdc-open-viz-module {
- div,
span,
applet,
object,
@@ -69,14 +68,7 @@
form,
label,
legend,
- table,
caption,
- tbody,
- tfoot,
- thead,
- tr,
- th,
- td,
article,
aside,
canvas,
diff --git a/packages/core/styles/base.scss b/packages/core/styles/base.scss
index bd3c59d15..44cdefdbe 100644
--- a/packages/core/styles/base.scss
+++ b/packages/core/styles/base.scss
@@ -71,7 +71,6 @@ body.post-type-cdc_visualization .cdc-editor .configure .editor-panel {
@include breakpointClass(md) {
font-size: 16px !important;
}
- @import 'data-table';
@import 'global';
@import 'button-section';
@import 'series-list';
diff --git a/packages/core/styles/v2/layout/index.scss b/packages/core/styles/v2/layout/index.scss
index b84347f3b..6775a4afb 100644
--- a/packages/core/styles/v2/layout/index.scss
+++ b/packages/core/styles/v2/layout/index.scss
@@ -1,5 +1,4 @@
@import 'alert';
@import 'component';
-@import 'data-table';
@import 'progression';
@import 'tooltip';
diff --git a/packages/editor/src/components/PreviewDataTable.tsx b/packages/editor/src/components/PreviewDataTable.tsx
index d10c10a45..cf1ec762b 100644
--- a/packages/editor/src/components/PreviewDataTable.tsx
+++ b/packages/editor/src/components/PreviewDataTable.tsx
@@ -24,7 +24,16 @@ const TableFilter = memo(({ globalFilter, setGlobalFilter, disabled = false }: a
setFilterValue(e.target.value)
}
- return
+ return (
+
+ )
})
const Header = memo(({ globalFilter, data, setGlobalFilter }: any) => (
@@ -38,7 +47,12 @@ const Footer = memo(({ previousPage, nextPage, canPreviousPage, canNextPage, pag