From e2f67eb5b7ff48dabde455e64761310f0b130e7a Mon Sep 17 00:00:00 2001 From: Ben Lister <147574410+benlister-okta@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:37:43 -0700 Subject: [PATCH 1/2] Update Roadmap JSON in Storybook (#2297) DES-6079 fix: update roadmap json in storybook fix: testing reducing complexity of table fix: debug table fix: debugging fix: update types fix: debug fix: testing fix: debug fix: debug fix: testing fix: testing fix: debugging fix: testing fix: debug fix: testing fix: testing fix: testing fix: update to resolve prod reload issue fix: resolve eslint error fix: update roadmap content 7/26 fix: update roadmap data fix: applitools delay added to fix VRT --- .../odyssey-storybook/applitools.config.js | 1 + .../src/guidelines/Roadmap/RoadmapTable.tsx | 219 +++--------------- .../src/guidelines/Roadmap/roadmap.json | 114 +++++---- .../src/guidelines/Roadmap/roadmapData.tsx | 182 ++++++++------- 4 files changed, 206 insertions(+), 310 deletions(-) diff --git a/packages/odyssey-storybook/applitools.config.js b/packages/odyssey-storybook/applitools.config.js index 7210a87c89..8270c58dd3 100644 --- a/packages/odyssey-storybook/applitools.config.js +++ b/packages/odyssey-storybook/applitools.config.js @@ -39,6 +39,7 @@ const applitoolsConfig = { runInDocker: true, serverUrl: "https://oktaeyes.applitools.com", testConcurrency: 20, + waitBeforeCapture: 1000, }; module.exports = applitoolsConfig; diff --git a/packages/odyssey-storybook/src/guidelines/Roadmap/RoadmapTable.tsx b/packages/odyssey-storybook/src/guidelines/Roadmap/RoadmapTable.tsx index 12b4572475..0f8683dc11 100644 --- a/packages/odyssey-storybook/src/guidelines/Roadmap/RoadmapTable.tsx +++ b/packages/odyssey-storybook/src/guidelines/Roadmap/RoadmapTable.tsx @@ -10,9 +10,10 @@ * See the License for the specific language governing permissions and limitations under the License. */ -import { DataFilter } from "@okta/odyssey-react-mui/labs"; -import { DataTable, DataTableSortingState } from "@okta/odyssey-react-mui"; -import { columns, data, OdysseyComponent } from "./roadmapData"; +/* eslint-disable import/no-extraneous-dependencies */ +import { memo, useCallback } from "react"; +import { Box, DataTable, DataTableGetDataType } from "@okta/odyssey-react-mui"; +import { useColumns, data, OdysseyComponent } from "./roadmapData"; import { Callout, CssBaseline, @@ -23,202 +24,28 @@ import { import { ThemeProvider as StorybookThemeProvider } from "@storybook/theming"; import * as odysseyTokens from "@okta/odyssey-design-tokens"; -const processData = ({ - initialData, - page = 1, - resultsPerPage = 100, - search, - filters, - sort, -}: { - initialData: OdysseyComponent[]; - page?: number; - resultsPerPage?: number; - search?: string; - filters?: DataFilter[]; - sort?: DataTableSortingState; -}) => { - let filteredData = [...initialData]; - - // Implement text-based query filtering - if (search) { - filteredData = filteredData.filter((row) => - Object.values(row).some((value) => - value.toString().toLowerCase().includes(search.toLowerCase()), - ), - ); - } - - // Implement column-specific filtering - if (filters) { - filteredData = filteredData.filter((row) => { - return filters.every(({ id, value }) => { - // If filter value is null or undefined, skip this filter - if (value === null || value === undefined) { - return true; - } - - // General filtering for other columns - return row[id as keyof OdysseyComponent] - ?.toString() - .includes(value.toString()); - }); - }); - } - - function parseCustomDate(dateStr: string): Date { - if (dateStr.length <= 0) { - return new Date(2999, 0); - } - - const months = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ]; - const [monthStr, yearStr] = dateStr.split(" "); - - const month = months.indexOf(monthStr); - const year = parseInt(yearStr.replace("'", ""), 10) + 2000; // Adjust for century - - return new Date(year, month); - } - - // Implement sorting - if (sort && sort.length > 0) { - filteredData.sort((a, b) => { - for (const { id, desc } of sort) { - let aValue: string | Date = a[id as keyof OdysseyComponent]; - let bValue: string | Date = b[id as keyof OdysseyComponent]; - - if ( - id === "startDate" || - id === "labsRelease" || - id === "fullRelease" - ) { - aValue = parseCustomDate(aValue); - bValue = parseCustomDate(bValue); - } - - if (aValue < bValue) return desc ? 1 : -1; - if (aValue > bValue) return desc ? -1 : 1; - } - - return 0; - }); - } - // Implement pagination - const startIdx = (page - 1) * resultsPerPage; - const endIdx = startIdx + resultsPerPage; - const paginatedData = filteredData.slice(startIdx, endIdx); - - return paginatedData; -}; - export const InnerRoadmapTable = () => { - // Constants for filter options - - const typeOptions = [ - { label: "Component", value: "Component" }, - { label: "Pattern", value: "Pattern" }, - ]; - - const statusOptions = [ - { label: "In Progress", value: "In progress" }, - { label: "In Labs", value: "In labs" }, - { label: "Released", value: "Released" }, - { label: "Not Started", value: "Not started" }, - ]; - - const expectedOptions = [ - { label: "FY24", value: "FY24" }, - { label: "TBD", value: "TBD" }, - { label: "Q1 FY25", value: "Q1 FY25" }, - { label: "Q2 FY25", value: "Q2 FY25" }, - { label: "Q3 FY25", value: "Q3 FY25" }, - { label: "Q4 FY25", value: "Q4 FY25" }, - { label: "Q1 FY26", value: "Q1 FY26" }, - { label: "Q2 FY26", value: "Q2 FY26" }, - { label: "Q3 FY26", value: "Q3 FY26" }, - { label: "Q4 FY26", value: "Q4 FY26" }, - ]; - - const fetchData = ({ - page, - resultsPerPage, - search, - filters, - sort, + const columns = useColumns(); // Use the hook to get columns + const filterData = ({ + data, }: { - page?: number; - resultsPerPage?: number; - search?: string; - filters?: DataFilter[]; - sort?: DataTableSortingState; - }) => { - return processData({ - initialData: data, - page: page, - resultsPerPage: resultsPerPage, - search: search, - filters: filters, - sort: sort, - }); + data: OdysseyComponent[]; + } & DataTableGetDataType) => { + const filteredData = data; + + return filteredData; }; - return ( - name} - getData={fetchData} - hasChangeableDensity={false} - hasColumnResizing={false} - hasColumnVisibility={false} - hasFilters - filters={[ - { - id: "type", - label: "Type", - variant: "select", - options: typeOptions, - }, - { - id: "status", - label: "Status", - variant: "select", - options: statusOptions, - }, - { - id: "deliverableTiming", - label: "Deliverable timing", - variant: "autocomplete", - options: expectedOptions, - }, - ]} - resultsPerPage={100} - hasPagination={false} - hasRowSelection={false} - hasRowReordering={false} - searchDelayTime={0} - hasSearch - hasSorting - /> - ); + const fetchData = useCallback(({ ...props }: DataTableGetDataType) => { + return filterData({ data, ...props }); + }, []); + + return ; }; const WrappedRoadmapTable = () => { const odysseyTheme = createOdysseyMuiTheme({ odysseyTokens }); - + const MemoizedInnerRoadmapTable = memo(InnerRoadmapTable); return ( {/* @ts-expect-error type mismatch on "typography" */} @@ -233,7 +60,15 @@ const WrappedRoadmapTable = () => { functionality, and you should not rely on them to make your purchase decisions. - + + + diff --git a/packages/odyssey-storybook/src/guidelines/Roadmap/roadmap.json b/packages/odyssey-storybook/src/guidelines/Roadmap/roadmap.json index 60bc6c0be7..f16812c3ea 100644 --- a/packages/odyssey-storybook/src/guidelines/Roadmap/roadmap.json +++ b/packages/odyssey-storybook/src/guidelines/Roadmap/roadmap.json @@ -5,7 +5,7 @@ "status": "Released", "define": "Complete", "design": "Complete", - "develop": "In progress", + "develop": "Complete", "deliverableTiming": "Q2 FY25" }, { @@ -65,11 +65,11 @@ { "name": "Card", "type": "Component", - "status": "In Labs", - "define": "In Labs", - "design": "In Labs", - "develop": "In Labs", - "deliverableTiming": "TBD" + "status": "In progress", + "define": "Complete", + "design": "Complete", + "develop": "Complete", + "deliverableTiming": "Q2 FY25" }, { "name": "Checkbox and checkbox group", @@ -96,15 +96,24 @@ "define": "Not started", "design": "Not started", "develop": "Not started", + "deliverableTiming": "Q3 FY25" + }, + { + "name": "Date picker", + "type": "Component", + "status": "In Labs", + "define": "Complete", + "design": "Complete", + "develop": "In progress", "deliverableTiming": "Q2 FY25" }, { - "name": "Data stack (data list, resource list?)", + "name": "Data stack", "type": "Component", - "status": "In progress", - "define": "In progress", - "design": "Not started", - "develop": "Not started", + "status": "In Labs", + "define": "Complete", + "design": "Complete", + "develop": "Complete", "deliverableTiming": "Q2 FY25" }, { @@ -117,11 +126,11 @@ "deliverableTiming": "Q1 FY25" }, { - "name": "Date picker", + "name": "Data view", "type": "Component", "status": "In Labs", "define": "Complete", - "design": "In progress", + "design": "Complete", "develop": "In progress", "deliverableTiming": "Q2 FY25" }, @@ -144,18 +153,18 @@ "deliverableTiming": "Q1 FY25" }, { - "name": "Duration picker\n(or fieldset)", + "name": "Duration' picker\n(or fieldset)", "type": "Component", - "status": "Not started", - "define": "Not started", - "design": "Not started", + "status": "In progress", + "define": "In progress", + "design": "In progress", "develop": "Not started", - "deliverableTiming": "Q2 FY25" + "deliverableTiming": "Q4 FY25" }, { "name": "Empty states v1 (no illustration)", "type": "Component", - "status": "In progress", + "status": "Released", "define": "Complete", "design": "Complete", "develop": "Complete", @@ -164,9 +173,9 @@ { "name": "Empty states v2 (illustration)", "type": "Component", - "status": "Not started", - "define": "Not started", - "design": "Not started", + "status": "In progress", + "define": "Complete", + "design": "In progress", "develop": "Not started", "deliverableTiming": "Q3 FY25" }, @@ -198,14 +207,32 @@ "deliverableTiming": "FY24" }, { - "name": "Form Layout (Advanced)", + "name": "Form layout (Advanced)", "type": "Component", "status": "In progress", + "define": "Complete", + "design": "In progress", + "develop": "In progress", + "deliverableTiming": "Q3 FY25" + }, + { + "name": "Group picker", + "type": "Component", + "status": "In Labs", "define": "In progress", - "design": "-", - "develop": "-", + "design": "In progress", + "develop": "In progress", "deliverableTiming": "Q2 FY25" }, + { + "name": "Inline text field", + "type": "Component", + "status": "In progress", + "define": "Complete", + "design": "Complete", + "develop": "In progress", + "deliverableTiming": "Q3 FY25" + }, { "name": "Link", "type": "Component", @@ -222,7 +249,7 @@ "define": "Not started", "design": "Not started", "develop": "Not started", - "deliverableTiming": "Q3 FY25" + "deliverableTiming": "Q4 FY25" }, { "name": "Menu button", @@ -236,7 +263,7 @@ { "name": "Multi-select dropdown", "type": "Component", - "status": "-", + "status": "Not started", "define": "-", "design": "-", "develop": "-", @@ -254,18 +281,18 @@ { "name": "Policy Recommender\n(AI Pattern)", "type": "Pattern", - "status": "Not started", - "define": "Not started", + "status": "In progress", + "define": "In progress", "design": "Not started", "develop": "Not started", - "deliverableTiming": "Q3 FY25" + "deliverableTiming": "Q4 FY25" }, { - "name": "Progress Bar", + "name": "Progress bar", "type": "Component", - "status": "Not started", - "define": "Not started", - "design": "Not started", + "status": "In progress", + "define": "In progress", + "design": "In progress", "develop": "Not started", "deliverableTiming": "Q3 FY25" }, @@ -278,6 +305,15 @@ "develop": "Complete", "deliverableTiming": "FY24" }, + { + "name": "Search input", + "type": "Component", + "status": "In progress", + "define": "Complete", + "design": "Complete", + "develop": "Complete", + "deliverableTiming": "Q2 FY25" + }, { "name": "Search field", "type": "Pattern", @@ -285,7 +321,7 @@ "define": "Not started", "design": "Not started", "develop": "Not started", - "deliverableTiming": "TBD" + "deliverableTiming": "Q3 FY25" }, { "name": "Select", @@ -318,10 +354,10 @@ "name": "Switch", "type": "Component", "status": "In Labs", - "define": "In Labs", + "define": "Complete", "design": "In Labs", "develop": "In Labs", - "deliverableTiming": "Q1 FY25" + "deliverableTiming": "Q2 FY25" }, { "name": "Tabs", @@ -351,13 +387,13 @@ "deliverableTiming": "FY24" }, { - "name": "Time Picker\n(or field set)", + "name": "Date Time Picker (name tbd)\n(or field set)", "type": "Component", "status": "Not started", "define": "Not started", "design": "Not started", "develop": "Not started", - "deliverableTiming": "Q2 FY25" + "deliverableTiming": "Q3 FY25" }, { "name": "Toast", diff --git a/packages/odyssey-storybook/src/guidelines/Roadmap/roadmapData.tsx b/packages/odyssey-storybook/src/guidelines/Roadmap/roadmapData.tsx index ec544499c0..6fee43629f 100644 --- a/packages/odyssey-storybook/src/guidelines/Roadmap/roadmapData.tsx +++ b/packages/odyssey-storybook/src/guidelines/Roadmap/roadmapData.tsx @@ -10,6 +10,8 @@ * See the License for the specific language governing permissions and limitations under the License. */ +/* eslint-disable import/no-extraneous-dependencies */ +import { useMemo } from "react"; import { DataTableRowData, Status, @@ -19,7 +21,6 @@ import { import { DataTableColumn } from "@okta/odyssey-react-mui"; import rawData from "./roadmap.json"; -export const data: OdysseyComponent[] = rawData as OdysseyComponent[]; export type OdysseyComponent = { name: string; @@ -31,86 +32,109 @@ export type OdysseyComponent = { deliverableTiming: string; }; -export const columns: DataTableColumn[] = [ - { - accessorKey: "name", - header: "Name", - enableHiding: false, - size: 325, - }, - { - accessorKey: "type", - header: "Type", - enableHiding: true, - size: 200, - }, - { - accessorKey: "status", - header: "Status", - size: 200, - Cell: ({ cell, row }) => { - const statusValue = cell.getValue(); - const defineValue = row.original.define; - const designValue = row.original.design; - const developValue = row.original.develop; - const severityMap = new Map< - string, - (typeof statusSeverityValues)[number] - >([ - ["Released", "success"], - ["In Labs", "warning"], - ["In progress", "default"], - ["Not started", "error"], - ]); - const severity = severityMap.get(statusValue) || "default"; +export const data: OdysseyComponent[] = rawData as OdysseyComponent[]; + +const severityMap = new Map([ + ["Released", "success"], + ["In Labs", "warning"], + ["In progress", "default"], + ["Not started", "error"], +]); + +const getTooltipText = ( + defineValue: string, + designValue: string, + developValue: string, +): string => { + let text = ""; + if (defineValue === "In progress") { + text += "Project definition in progress"; + } + if (["Complete", "In progress"].includes(designValue)) { + text += (text ? " " : "") + "Design: " + designValue; + } + if (["Complete", "In progress"].includes(developValue)) { + text += (text ? " " : "") + "Develop: " + developValue; + } + return text.trim(); +}; + +type CellProps = { + cell: { getValue: () => string }; + row: { original: OdysseyComponent }; +}; - // First priority: Check if the define stage is "In Progress" - if (defineValue === "In Progress") { - // Return a Tooltip specifically for this condition and do nothing else - return ( - - - - ); - } +const StatusCell: React.FC = ({ cell, row }) => { + const statusValue = cell.getValue(); + const { + define: defineValue, + design: designValue, + develop: developValue, + } = row.original; - // If defineValue is not "In Progress", then proceed with this logic: - let tooltipText = ""; - if (defineValue === "In progress") { - tooltipText += "Project definition in progress"; - } - if (["Complete", "In progress"].includes(designValue)) { - tooltipText += "Design: " + designValue + " "; - } - if (["Complete", "In progress"].includes(developValue)) { - tooltipText += "Develop: " + developValue; - } + const severity = useMemo( + () => severityMap.get(statusValue) || "default", + [statusValue], + ); + const tooltipText = useMemo( + () => getTooltipText(defineValue, designValue, developValue), + [defineValue, designValue, developValue], + ); - // Only show the tooltip if there's relevant information to display - if (tooltipText && statusValue !== "Released") { - return ( - - - - ); - } + if (defineValue === "In Progress") { + return ( + + + + ); + } - // If there is no relevant tooltip text, just show the status without any tooltip - return ; - }, - }, + if (tooltipText && statusValue !== "Released") { + return ( + + + + ); + } - { - accessorKey: "deliverableTiming", - header: "Deliverable timing", - enableHiding: false, - size: 200, - }, -]; + return ; +}; + +export const useColumns = (): DataTableColumn[] => { + return useMemo( + () => + [ + { + accessorKey: "name", + header: "Name", + enableHiding: false, + size: 290, + }, + { + accessorKey: "type", + header: "Type", + enableHiding: true, + size: 110, + }, + { + accessorKey: "status", + header: "Status", + size: 115, + Cell: ({ cell, row }: CellProps) => ( + + ), + }, + { + accessorKey: "deliverableTiming", + header: "Deliverable timing", + enableHiding: false, + size: 155, + }, + ] as DataTableColumn[], + [], + ); +}; From 0c9a10ca9036e34f74dd383f26dda8dc77211c90 Mon Sep 17 00:00:00 2001 From: Kevin Ghadyani <97464104+KevinGhadyani-Okta@users.noreply.github.com> Date: Tue, 6 Aug 2024 14:08:18 -0500 Subject: [PATCH 2/2] fix: Applitools isn't consistently running VRTs (#2314) OKTA-756835 build: adds wait before Applitools captures in VRTs fix: disables Storybook telemetry build: fixes Applitools incorrect parentBranchName when no PR build: removed "abort commit on failure" Merge remote-tracking branch 'origin/main' into kg_applitools-consistency fix: adds 2 second delay before capturing for Applitools Merge remote-tracking branch 'origin/main' into kg_applitools-consistency --- .bacon.yml | 11 +---------- packages/odyssey-storybook/.storybook/main.ts | 11 +++++++---- packages/odyssey-storybook/applitools.config.js | 12 +++++++----- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/.bacon.yml b/.bacon.yml index a2a374f03d..6c430d2fe0 100644 --- a/.bacon.yml +++ b/.bacon.yml @@ -1,6 +1,5 @@ test_suites: - name: "Build" - abort_commit_on_failure: true criteria: MERGE queue_name: ci-queue-productionJenga-AL2023 script_path: /root/okta/odyssey/scripts @@ -15,7 +14,6 @@ test_suites: timeout: "20" - name: "Code Coverage" - abort_commit_on_failure: true criteria: OPTIONAL queue_name: ci-queue-prodJenga-Ubuntu20 # Playwright only works with certain distros of Linux script_name: code-coverage @@ -24,7 +22,6 @@ test_suites: timeout: "20" - name: "Interaction Test" - abort_commit_on_failure: true criteria: MERGE queue_name: ci-queue-prodJenga-Ubuntu20 # Playwright only works with certain distros of Linux script_path: /root/okta/odyssey/scripts @@ -33,7 +30,6 @@ test_suites: timeout: "20" - name: "Lint" - abort_commit_on_failure: true criteria: MERGE queue_name: ci-queue-productionJenga-AL2023 script_path: /root/okta/odyssey/scripts @@ -48,7 +44,6 @@ test_suites: timeout: "20" - name: "Publish Package" - abort_commit_on_failure: true criteria: MERGE queue_name: ci-queue-productionJenga-AL2023 script_name: publish-packages @@ -57,7 +52,6 @@ test_suites: timeout: "15" - name: "Publish Storybook" - abort_commit_on_failure: true criteria: MERGE queue_name: ci-queue-productionJenga-AL2023 script_name: publish-storybook @@ -70,11 +64,10 @@ test_suites: queue_name: ci-queue-productionJenga-AL2023 script_name: semgrep script_path: /root/okta/odyssey/scripts - sort_order: "7" + sort_order: "10" timeout: "10" - name: "Test" - abort_commit_on_failure: true criteria: MERGE queue_name: ci-queue-productionJenga-AL2023 script_name: unit-test @@ -83,7 +76,6 @@ test_suites: timeout: "20" - name: "Typecheck" - abort_commit_on_failure: true criteria: MERGE queue_name: ci-queue-productionJenga-AL2023 script_path: /root/okta/odyssey/scripts @@ -98,7 +90,6 @@ test_suites: timeout: "20" - name: "Visual Regression Test" - abort_commit_on_failure: true criteria: MERGE queue_name: ci-queue-productionJenga-AL2023 script_name: visual-regression-test diff --git a/packages/odyssey-storybook/.storybook/main.ts b/packages/odyssey-storybook/.storybook/main.ts index a88bca52c6..98ea2bedff 100644 --- a/packages/odyssey-storybook/.storybook/main.ts +++ b/packages/odyssey-storybook/.storybook/main.ts @@ -22,7 +22,6 @@ const getAbsolutePath = (value: string): any => { }; const config: StorybookConfig = { - stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], addons: [ { name: "@storybook/addon-docs", @@ -37,10 +36,17 @@ const config: StorybookConfig = { getAbsolutePath("@storybook/addon-mdx-gfm"), getAbsolutePath("storybook-addon-rtl-direction"), ], + core: { + disableTelemetry: true, + }, + docs: { + autodocs: true, + }, framework: { name: getAbsolutePath("@storybook/react-vite"), options: {}, }, + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], typescript: { check: false, reactDocgen: "react-docgen-typescript", @@ -55,9 +61,6 @@ const config: StorybookConfig = { }, }, }, - docs: { - autodocs: true, - }, }; export default config; diff --git a/packages/odyssey-storybook/applitools.config.js b/packages/odyssey-storybook/applitools.config.js index 8270c58dd3..4c797ff069 100644 --- a/packages/odyssey-storybook/applitools.config.js +++ b/packages/odyssey-storybook/applitools.config.js @@ -10,11 +10,12 @@ * See the License for the specific language governing permissions and limitations under the License. */ -const branchName = - process.env.GITHUB_HEAD_REF ?? process.env.CURRENT_BRANCH_NAME; +const branchName = process.env.CURRENT_BRANCH_NAME; const parentBranchName = - process.env.GITHUB_BASE_REF ?? process.env.BASE_BRANCH_NAME ?? "main"; -const commitHash = process.env.GITHUB_SHA ?? process.env.SHA; + process.env.BASE_BRANCH_NAME === "null" || !process.env.BASE_BRANCH_NAME + ? "main" + : process.env.BASE_BRANCH_NAME; +const commitHash = process.env.SHA; const applitoolsConfig = { accessibilityValidation: { @@ -37,9 +38,10 @@ const applitoolsConfig = { headless: true, }, runInDocker: true, + saveNewTests: true, serverUrl: "https://oktaeyes.applitools.com", testConcurrency: 20, - waitBeforeCapture: 1000, + waitBeforeCapture: 2000, }; module.exports = applitoolsConfig;