This is an automated email from the ASF dual-hosted git repository. vogievetsky pushed a commit to branch segment_timeline2 in repository https://gitbox.apache.org/repos/asf/druid.git
commit 8ff3f44ec39e13fca3d2cec73a9682f00616987f Author: Vadim Ogievetsky <[email protected]> AuthorDate: Wed Oct 30 11:55:22 2024 -0700 better timezone menu --- web-console/jest.common.config.js | 2 + .../__snapshots__/bar-unit.spec.tsx.snap | 13 - .../__snapshots__/segment-timeline.spec.tsx.snap | 293 ++++++++++----------- web-console/src/utils/general.tsx | 8 + .../__snapshots__/datasources-view.spec.tsx.snap | 15 +- .../__snapshots__/segments-view.spec.tsx.snap | 50 ++-- .../views/workbench-view/run-panel/run-panel.tsx | 71 +---- .../timezone-menu-items.spec.tsx} | 30 ++- .../timezone-menu-items/timezone-menu-items.tsx | 135 ++++++++++ 9 files changed, 355 insertions(+), 262 deletions(-) diff --git a/web-console/jest.common.config.js b/web-console/jest.common.config.js index 89e3dab5852..1ea8f55ad18 100644 --- a/web-console/jest.common.config.js +++ b/web-console/jest.common.config.js @@ -18,6 +18,8 @@ const { createJsWithTsPreset } = require('ts-jest'); +process.env.TZ = 'UTC'; + module.exports = { testEnvironment: 'jsdom', transformIgnorePatterns: ['/node_modules/(?!(d3-.+)/)'], diff --git a/web-console/src/components/segment-timeline/__snapshots__/bar-unit.spec.tsx.snap b/web-console/src/components/segment-timeline/__snapshots__/bar-unit.spec.tsx.snap deleted file mode 100644 index 7d98145d4b5..00000000000 --- a/web-console/src/components/segment-timeline/__snapshots__/bar-unit.spec.tsx.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`BarUnit matches snapshot 1`] = ` -<svg> - <rect - class="bar-unit" - height="10" - width="10" - x="10" - y="10" - /> -</svg> -`; diff --git a/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap b/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap index ae30df246c2..b82bfbc10f2 100644 --- a/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap +++ b/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap @@ -2,190 +2,179 @@ exports[`SegmentTimeline matches snapshot 1`] = ` <div - class="segment-timeline" + class="splitter-layout segment-timeline splitter-layout-horizontal" > - <div> + <div + class="layout-pane layout-pane-primary" + > <div - class="loader" - > - <div - class="loader-logo" - > - <svg - viewBox="0 0 100 100" - > - <path - class="one" - d="M54.2,69.8h-2.7c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h2.7c11.5,0,23.8-7.4,23.8-23.7 - c0-9.1-6.9-15.8-16.4-15.8H38c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h23.6c5.3,0,10.1,1.9,13.6,5.3c3.5,3.4,5.4,8,5.4,13.1 - c0,6.6-2.3,13-6.3,17.7C69.5,66.8,62.5,69.8,54.2,69.8z" - /> - <path - class="two" - d="M55.7,59.5h-26c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h26c7.5,0,11.5-5.8,11.5-11.5 - c0-4.2-3.2-7.3-7.7-7.3h-26c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h26c5.9,0,10.3,4.3,10.3,9.9c0,3.7-1.3,7.2-3.7,9.8 - C63.5,58,59.9,59.5,55.7,59.5z" - /> - <path - class="three" - d="M27.2,38h-6.3c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h6.3c0.7,0,1.3,0.6,1.3,1.3S27.9,38,27.2,38z" - /> - <path - class="four" - d="M45.1,69.8h-5.8c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h5.8c0.7,0,1.3,0.6,1.3,1.3 - C46.4,69.2,45.8,69.8,45.1,69.8z" - /> - </svg> - </div> - </div> + class="chart-container" + /> </div> <div - class="side-control" + class="layout-splitter" + role="separator" + /> + <div + class="layout-pane" + style="width: 220px;" > <div - class="bp5-form-group" + class="side-control" > - <label - class="bp5-label" - > - Show - - <span - class="bp5-text-muted" - /> - </label> <div - class="bp5-form-content" + class="bp5-form-group" > + <label + class="bp5-label" + > + Show + + <span + class="bp5-text-muted" + /> + </label> <div - class="bp5-segmented-control bp5-fill" - role="radiogroup" + class="bp5-form-content" > - <button - aria-checked="true" - class="bp5-button" - role="radio" - tabindex="0" - type="button" + <div + class="bp5-segmented-control bp5-fill" + role="radiogroup" > - <span - class="bp5-button-text" + <button + aria-checked="true" + class="bp5-button" + role="radio" + tabindex="0" + type="button" > - Total size - </span> - </button> - <button - aria-checked="false" - class="bp5-button bp5-minimal" - role="radio" - tabindex="-1" - type="button" - > - <span - class="bp5-button-text" + <span + class="bp5-button-text" + > + Size + </span> + </button> + <button + aria-checked="false" + class="bp5-button bp5-minimal" + role="radio" + tabindex="-1" + type="button" > - Segment count - </span> - </button> + <span + class="bp5-button-text" + > + Rows + </span> + </button> + <button + aria-checked="false" + class="bp5-button bp5-minimal" + role="radio" + tabindex="-1" + type="button" + > + <span + class="bp5-button-text" + > + Count + </span> + </button> + </div> </div> </div> - </div> - <div - class="bp5-form-group" - > - <label - class="bp5-label" - > - Interval - - <span - class="bp5-text-muted" - /> - </label> <div - class="bp5-form-content" + class="bp5-form-group" > + <label + class="bp5-label" + > + Interval + + <span + class="bp5-text-muted" + /> + </label> <div - aria-expanded="false" - aria-haspopup="menu" - class="bp5-control-group bp5-date-range-input bp5-popover-target bp5-fill" + class="bp5-form-content" > <div - class="bp5-input-group bp5-fill" + aria-expanded="false" + aria-haspopup="menu" + class="bp5-control-group bp5-date-range-input bp5-popover-target bp5-fill" > - <input - aria-disabled="false" - autocomplete="off" - class="bp5-input" - placeholder="Start date" - type="text" - value="2021-03-09" - /> - </div> - <div - class="bp5-input-group bp5-fill" - > - <input - aria-disabled="false" - autocomplete="off" - class="bp5-input" - placeholder="End date" - type="text" - value="2021-06-09" - /> + <div + class="bp5-input-group bp5-fill" + > + <input + aria-disabled="false" + autocomplete="off" + class="bp5-input" + placeholder="Start date" + type="text" + value="2024-07-31" + /> + </div> + <div + class="bp5-input-group bp5-fill" + > + <input + aria-disabled="false" + autocomplete="off" + class="bp5-input" + placeholder="End date" + type="text" + value="2024-10-31" + /> + </div> </div> </div> </div> - </div> - <div - class="bp5-form-group" - > - <label - class="bp5-label" - > - Datasource - - <span - class="bp5-text-muted" - /> - </label> <div - class="bp5-form-content" + class="bp5-form-group" > + <label + class="bp5-label" + > + Datasource + + <span + class="bp5-text-muted" + /> + </label> <div - aria-controls="listbox-1" - aria-expanded="false" - aria-haspopup="listbox" - class="bp5-popover-target bp5-fill" - role="combobox" + class="bp5-form-content" > - <button - class="bp5-button bp5-fill" - type="button" + <div + aria-controls="listbox-0" + aria-expanded="false" + aria-haspopup="listbox" + class="bp5-popover-target bp5-fill" + role="combobox" > - <span - class="bp5-button-text" + <button + class="bp5-button bp5-fill" + type="button" > - Show all - </span> - <span - aria-hidden="true" - class="bp5-icon bp5-icon-caret-down" - > - <svg - data-icon="caret-down" - height="16" - role="img" - viewBox="0 0 16 16" - width="16" + <span + aria-hidden="true" + class="bp5-icon bp5-icon-caret-down" > - <path - d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z" - fill-rule="evenodd" - /> - </svg> - </span> - </button> + <svg + data-icon="caret-down" + height="16" + role="img" + viewBox="0 0 16 16" + width="16" + > + <path + d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z" + fill-rule="evenodd" + /> + </svg> + </span> + </button> + </div> </div> </div> </div> diff --git a/web-console/src/utils/general.tsx b/web-console/src/utils/general.tsx index b742013b2e8..0bc959b6045 100644 --- a/web-console/src/utils/general.tsx +++ b/web-console/src/utils/general.tsx @@ -373,6 +373,14 @@ export function formatDurationHybrid(ms: NumberLike): string { } } +export function timezoneOffsetInMinutesToString(offsetInMinutes: number, padHour: boolean): string { + const sign = offsetInMinutes < 0 ? '-' : '+'; + const absOffset = Math.abs(offsetInMinutes); + const h = Math.floor(absOffset / 60); + const m = absOffset % 60; + return `${sign}${padHour ? pad2(h) : h}:${pad2(m)}`; +} + function pluralize(word: string): string { // Ignoring irregular plurals. if (/(s|x|z|ch|sh)$/.test(word)) { diff --git a/web-console/src/views/datasources-view/__snapshots__/datasources-view.spec.tsx.snap b/web-console/src/views/datasources-view/__snapshots__/datasources-view.spec.tsx.snap index 6da41f6eb76..c4a548ad886 100644 --- a/web-console/src/views/datasources-view/__snapshots__/datasources-view.spec.tsx.snap +++ b/web-console/src/views/datasources-view/__snapshots__/datasources-view.spec.tsx.snap @@ -55,7 +55,7 @@ exports[`DatasourcesView matches snapshot 1`] = ` onChange={[Function]} /> <Blueprint5.Switch - checked={false} + checked={true} disabled={false} label="Show segment timeline" onChange={[Function]} @@ -109,6 +109,7 @@ exports[`DatasourcesView matches snapshot 1`] = ` /> </Memo(ViewControlBar)> <SplitterLayout + className="timeline-datasources-splitter" percentage={true} primaryIndex={1} primaryMinSize={20} @@ -116,6 +117,18 @@ exports[`DatasourcesView matches snapshot 1`] = ` secondaryMinSize={10} vertical={true} > + <SegmentTimeline + capabilities={ + Capabilities { + "coordinator": true, + "maxTaskSlots": undefined, + "multiStageQueryDart": true, + "multiStageQueryTask": true, + "overlord": true, + "queryType": "nativeAndSql", + } + } + /> <ReactTable AggregatedComponent={[Function]} ExpanderComponent={[Function]} diff --git a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap index e177003b7ee..7fa71826cb1 100755 --- a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap +++ b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap @@ -54,10 +54,7 @@ exports[`SegmentsView matches snapshot 1`] = ` "Start", "End", "Version", - { - "label": "𝑓(sys.segments)", - "text": "Time span", - }, + "Time span", "Shard type", "Shard spec", "Partition", @@ -77,7 +74,6 @@ exports[`SegmentsView matches snapshot 1`] = ` onClose={[Function]} tableColumnsHidden={ [ - "Time span", "Is published", "Is overshadowed", ] @@ -85,6 +81,7 @@ exports[`SegmentsView matches snapshot 1`] = ` /> </Memo(ViewControlBar)> <SplitterLayout + className="timeline-segments-splitter" percentage={true} primaryIndex={1} primaryMinSize={20} @@ -184,7 +181,7 @@ exports[`SegmentsView matches snapshot 1`] = ` "headerClassName": "enable-comparisons", "show": true, "sortable": true, - "width": 160, + "width": 180, }, { "Cell": [Function], @@ -195,7 +192,7 @@ exports[`SegmentsView matches snapshot 1`] = ` "headerClassName": "enable-comparisons", "show": true, "sortable": true, - "width": 160, + "width": 180, }, { "Cell": [Function], @@ -205,15 +202,16 @@ exports[`SegmentsView matches snapshot 1`] = ` "filterable": true, "show": true, "sortable": true, - "width": 160, + "width": 180, }, { - "Cell": [Function], "Header": "Time span", - "accessor": "time_span", - "filterable": true, - "show": false, - "sortable": true, + "accessor": [Function], + "className": "padded", + "filterable": false, + "id": "time_span", + "show": true, + "sortable": false, "width": 100, }, { @@ -374,18 +372,11 @@ exports[`SegmentsView matches snapshot 1`] = ` defaultFilterMethod={[Function]} defaultFiltered={[]} defaultPage={0} - defaultPageSize={50} + defaultPageSize={20} defaultResized={[]} defaultSortDesc={false} defaultSortMethod={[Function]} - defaultSorted={ - [ - { - "desc": true, - "id": "start", - }, - ] - } + defaultSorted={[]} expanderDefaults={ { "filterable": false, @@ -421,7 +412,7 @@ exports[`SegmentsView matches snapshot 1`] = ` getTrProps={[Function]} groupedByPivotKey="_groupedByPivot" indexKey="_index" - loading={false} + loading={true} loadingText="Loading..." manual={true} multiSort={true} @@ -431,8 +422,13 @@ exports[`SegmentsView matches snapshot 1`] = ` ofText="" onFetchData={[Function]} onFilteredChange={[Function]} + onPageChange={[Function]} + onPageSizeChange={[Function]} + onSortedChange={[Function]} originalKey="_original" + page={0} pageJumpText="jump to page" + pageSize={50} pageSizeOptions={ [ 50, @@ -457,6 +453,14 @@ exports[`SegmentsView matches snapshot 1`] = ` showPaginationBottom={true} showPaginationTop={false} sortable={true} + sorted={ + [ + { + "desc": true, + "id": "start", + }, + ] + } style={{}} subRowsKey="_subRows" /> diff --git a/web-console/src/views/workbench-view/run-panel/run-panel.tsx b/web-console/src/views/workbench-view/run-panel/run-panel.tsx index d586959357a..2747179d6d4 100644 --- a/web-console/src/views/workbench-view/run-panel/run-panel.tsx +++ b/web-console/src/views/workbench-view/run-panel/run-panel.tsx @@ -51,31 +51,10 @@ import { deepGet, pluralIfNeeded, removeUndefinedValues, tickIcon } from '../../ import type { MaxTasksButtonProps } from '../max-tasks-button/max-tasks-button'; import { MaxTasksButton } from '../max-tasks-button/max-tasks-button'; import { QueryParametersDialog } from '../query-parameters-dialog/query-parameters-dialog'; +import { TimezoneMenuItems } from '../timezone-menu-items/timezone-menu-items'; import './run-panel.scss'; -const NAMED_TIMEZONES: string[] = [ - 'America/Juneau', // -9.0 - 'America/Los_Angeles', // -8.0 - 'America/Yellowknife', // -7.0 - 'America/Phoenix', // -7.0 - 'America/Denver', // -7.0 - 'America/Mexico_City', // -6.0 - 'America/Chicago', // -6.0 - 'America/New_York', // -5.0 - 'America/Argentina/Buenos_Aires', // -4.0 - 'Etc/UTC', // +0.0 - 'Europe/London', // +0.0 - 'Europe/Paris', // +1.0 - 'Asia/Jerusalem', // +2.0 - 'Asia/Shanghai', // +8.0 - 'Asia/Hong_Kong', // +8.0 - 'Asia/Seoul', // +9.0 - 'Asia/Tokyo', // +9.0 - 'Pacific/Guam', // +10.0 - 'Australia/Sydney', // +11.0 -]; - const ARRAY_INGEST_MODE_LABEL: Record<ArrayIngestMode, string> = { array: 'Array', mvd: 'MVD', @@ -314,25 +293,6 @@ export const RunPanel = React.memo(function RunPanel(props: RunPanelProps) { onQueryChange(query.changeQueryContext(removeUndefinedValues(queryContext))); } - function offsetOptions(): JSX.Element[] { - const items: JSX.Element[] = []; - - for (let i = -12; i <= 14; i++) { - const offset = `${i < 0 ? '-' : '+'}${String(Math.abs(i)).padStart(2, '0')}:00`; - items.push( - <MenuItem - key={offset} - icon={tickIcon(offset === sqlTimeZone)} - text={offset} - shouldDismissPopover={false} - onClick={() => changeQueryContext({ ...queryContext, sqlTimeZone: offset })} - />, - ); - } - - return items; - } - const overloadWarning = query.unlimited && (queryEngine === 'sql-native' || @@ -424,32 +384,13 @@ export const RunPanel = React.memo(function RunPanel(props: RunPanelProps) { text="Timezone" label={sqlTimeZone ?? defaultQueryContext.sqlTimeZone} > - <MenuDivider title="Timezone type" /> - <MenuItem - icon={tickIcon(!sqlTimeZone)} - text="Default" - label={defaultQueryContext.sqlTimeZone} - shouldDismissPopover={false} - onClick={() => - changeQueryContext({ ...queryContext, sqlTimeZone: undefined }) + <TimezoneMenuItems + sqlTimeZone={sqlTimeZone} + setSqlTimeZone={sqlTimeZone => + changeQueryContext({ ...queryContext, sqlTimeZone }) } + defaultSqlTimeZone={defaultQueryContext.sqlTimeZone} /> - <MenuItem icon={tickIcon(String(sqlTimeZone).includes('/'))} text="Named"> - {NAMED_TIMEZONES.map(namedTimezone => ( - <MenuItem - key={namedTimezone} - icon={tickIcon(namedTimezone === sqlTimeZone)} - text={namedTimezone} - shouldDismissPopover={false} - onClick={() => - changeQueryContext({ ...queryContext, sqlTimeZone: namedTimezone }) - } - /> - ))} - </MenuItem> - <MenuItem icon={tickIcon(String(sqlTimeZone).includes(':'))} text="Offset"> - {offsetOptions()} - </MenuItem> <MenuItem icon={IconNames.BLANK} text="Custom" diff --git a/web-console/jest.common.config.js b/web-console/src/views/workbench-view/timezone-menu-items/timezone-menu-items.spec.tsx similarity index 58% copy from web-console/jest.common.config.js copy to web-console/src/views/workbench-view/timezone-menu-items/timezone-menu-items.spec.tsx index 89e3dab5852..5a1a2b9ce51 100644 --- a/web-console/jest.common.config.js +++ b/web-console/src/views/workbench-view/timezone-menu-items/timezone-menu-items.spec.tsx @@ -16,12 +16,26 @@ * limitations under the License. */ -const { createJsWithTsPreset } = require('ts-jest'); +import { shallow } from '../../../utils/shallow-renderer'; -module.exports = { - testEnvironment: 'jsdom', - transformIgnorePatterns: ['/node_modules/(?!(d3-.+)/)'], - ...createJsWithTsPreset({ - tsconfig: './tsconfig.test.json', - }), -}; +import { TimezoneMenuItems } from './timezone-menu-items'; + +jest.useFakeTimers('modern').setSystemTime(Date.parse('2024-06-08T12:34:56Z')); + +describe('TimezoneMenuItems', () => { + it('ensure UTC', () => { + expect(new Date().getTimezoneOffset()).toBe(0); + }); + + it('matches snapshot', () => { + const comp = shallow( + <TimezoneMenuItems + sqlTimeZone="Blah" + setSqlTimeZone={() => {}} + defaultSqlTimeZone="Etc/UTC" + />, + ); + + expect(comp).toMatchSnapshot(); + }); +}); diff --git a/web-console/src/views/workbench-view/timezone-menu-items/timezone-menu-items.tsx b/web-console/src/views/workbench-view/timezone-menu-items/timezone-menu-items.tsx new file mode 100644 index 00000000000..402620563ef --- /dev/null +++ b/web-console/src/views/workbench-view/timezone-menu-items/timezone-menu-items.tsx @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Icon, MenuDivider, MenuItem } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; +import { fromDate, getLocalTimeZone } from '@internationalized/date'; + +import { minute, tickIcon, timezoneOffsetInMinutesToString } from '../../../utils'; + +const n = new Date(); +const NAMED_TIMEZONES: { timezone: string; offsetInMinutes: number }[] = [ + 'America/Juneau', // -9:00 + 'America/Los_Angeles', // -8:00 + 'America/Yellowknife', // -7:00 + 'America/Phoenix', // -7:00 + 'America/Denver', // -7:00 + 'America/Mexico_City', // -6:00 + 'America/Chicago', // -6:00 + 'America/New_York', // -5:00 + 'America/Argentina/Buenos_Aires', // -4:00 + 'Etc/UTC', // +0:00 + 'Europe/London', // +0:00 + 'Europe/Paris', // +1:00 + 'Asia/Jerusalem', // +2:00 + 'Asia/Kolkata', // +5:30 + 'Asia/Shanghai', // +8:00 + 'Asia/Hong_Kong', // +8:00 + 'Asia/Seoul', // +9:00 + 'Asia/Tokyo', // +9:00 + 'Pacific/Guam', // +10:00 + 'Australia/Sydney', // +11:00 +].map(timezone => ({ + timezone, + offsetInMinutes: fromDate(n, timezone).offset / minute.canonicalLength, +})); +const LOCAL_TIMEZONE = getLocalTimeZone(); +const LOCAL_OFFSET_IN_MINUTES = -n.getTimezoneOffset(); + +// Make sure the browsers timezone is in the list +if (!NAMED_TIMEZONES.find(({ timezone }) => timezone === LOCAL_TIMEZONE)) { + NAMED_TIMEZONES.push({ timezone: LOCAL_TIMEZONE, offsetInMinutes: LOCAL_OFFSET_IN_MINUTES }); +} +NAMED_TIMEZONES.sort((a, b) => a.offsetInMinutes - b.offsetInMinutes); + +const OFFSETS_IN_MINUTES: number[] = []; +for (let offsetInMinutes = -12 * 60; offsetInMinutes <= 14 * 60; offsetInMinutes += 60) { + OFFSETS_IN_MINUTES.push(offsetInMinutes); +} + +// Make sure the browser offset is in the list +if (!OFFSETS_IN_MINUTES.includes(LOCAL_OFFSET_IN_MINUTES)) { + OFFSETS_IN_MINUTES.push(LOCAL_OFFSET_IN_MINUTES); + OFFSETS_IN_MINUTES.sort((a, b) => a - b); +} + +export interface TimezoneMenuItemsProps { + sqlTimeZone: string | undefined; + setSqlTimeZone(sqlTimeZone: string | undefined): void; + defaultSqlTimeZone: string | undefined; +} + +export const TimezoneMenuItems = function TimezoneMenuItems(props: TimezoneMenuItemsProps) { + const { sqlTimeZone, setSqlTimeZone, defaultSqlTimeZone } = props; + + const localOffset = new Date().getTimezoneOffset(); + + return [ + <MenuDivider key="type" title="Timezone type" />, + <MenuItem + key="default" + icon={tickIcon(!sqlTimeZone)} + text="Default" + label={defaultSqlTimeZone} + shouldDismissPopover={false} + onClick={() => setSqlTimeZone(undefined)} + />, + <MenuItem key="named" icon={tickIcon(String(sqlTimeZone).includes('/'))} text="Named"> + {NAMED_TIMEZONES.map(({ timezone, offsetInMinutes }) => ( + <MenuItem + key={timezone} + icon={tickIcon(timezone === sqlTimeZone)} + text={ + timezone === LOCAL_TIMEZONE ? ( + <> + {timezone} <Icon icon={IconNames.STAR} data-tooltip="Browser timezone" /> + </> + ) : ( + timezone + ) + } + label={`UTC${timezoneOffsetInMinutesToString(offsetInMinutes, false)}`} + shouldDismissPopover={false} + onClick={() => setSqlTimeZone(timezone)} + /> + ))} + </MenuItem>, + <MenuItem key="offset" icon={tickIcon(String(sqlTimeZone).includes(':'))} text="Offset"> + {OFFSETS_IN_MINUTES.map(offsetInMinutes => { + const offset = timezoneOffsetInMinutesToString(offsetInMinutes, true); + return ( + <MenuItem + key={offset} + icon={tickIcon(offset === sqlTimeZone)} + text={ + localOffset === offsetInMinutes ? ( + <> + {offset} <Icon icon={IconNames.STAR} data-tooltip="Browser offset" /> + </> + ) : ( + offset + ) + } + shouldDismissPopover={false} + onClick={() => setSqlTimeZone(offset)} + /> + ); + })} + </MenuItem>, + ]; +}; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
