diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/default-full-width.e2e.tsx b/e2e/nextjs-app/src/app/components/uneditable-section/default-full-width.e2e.tsx new file mode 100644 index 0000000000..33378c8125 --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/default-full-width.e2e.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"; +import { items } from "./uneditable-section-shared"; + +export default function Story() { + return ( +
+ +
+ ); +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/default-layout.e2e.tsx b/e2e/nextjs-app/src/app/components/uneditable-section/default-layout.e2e.tsx new file mode 100644 index 0000000000..2478272115 --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/default-layout.e2e.tsx @@ -0,0 +1,17 @@ +"use client"; + +import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"; +import { items } from "./uneditable-section-shared"; + +export default function Story() { + return ( +
+ +
+ ); +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/default-no-background.e2e.tsx b/e2e/nextjs-app/src/app/components/uneditable-section/default-no-background.e2e.tsx new file mode 100644 index 0000000000..0ea2a8d3fb --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/default-no-background.e2e.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"; +import { items } from "./uneditable-section-shared"; + +export default function Story() { + return ( +
+ +
+ ); +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/error-alert-variant.e2e.tsx b/e2e/nextjs-app/src/app/components/uneditable-section/error-alert-variant.e2e.tsx new file mode 100644 index 0000000000..7a870de1a4 --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/error-alert-variant.e2e.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"; + +export default function Story() { + return ( + + ); +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/full-custom-sections.e2e.tsx b/e2e/nextjs-app/src/app/components/uneditable-section/full-custom-sections.e2e.tsx new file mode 100644 index 0000000000..3ec2bf519f --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/full-custom-sections.e2e.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { Alert } from "@lifesg/react-design-system/alert"; +import { Button } from "@lifesg/react-design-system/button"; +import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"; +import styles from "./full-custom-sections.module.css"; + +export default function Story() { + return ( + +
+
+

My custom content

+ +
+
+ Custom alert +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Etiam pellentesque enim eu neque gravida, ut pulvinar magna + tristique. Aenean sed malesuada arcu. Integer convallis + dapibus suscipit. +

+ + + + + +

Another section

+ + +
  • Option 1
  • +
  • Option 2
  • +
  • Option 3
  • + + } + /> +
    +
    +
    + ); +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/full-custom-sections.module.css b/e2e/nextjs-app/src/app/components/uneditable-section/full-custom-sections.module.css new file mode 100644 index 0000000000..410d1fc8ff --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/full-custom-sections.module.css @@ -0,0 +1,16 @@ +.custom-style-1 { + display: flex; + flex-direction: column; + grid-column: span 8; +} + +.custom-style-2 { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 2rem; +} + +.custom-style-3 { + margin-bottom: 1rem; +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/loading-variant.e2e.tsx b/e2e/nextjs-app/src/app/components/uneditable-section/loading-variant.e2e.tsx new file mode 100644 index 0000000000..d0269b442e --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/loading-variant.e2e.tsx @@ -0,0 +1,22 @@ +"use client"; + +import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"; + +export default function Story() { + return ( + + ); +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/masked-variants.e2e.tsx b/e2e/nextjs-app/src/app/components/uneditable-section/masked-variants.e2e.tsx new file mode 100644 index 0000000000..2cef1e6515 --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/masked-variants.e2e.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"; + +export default function Story() { + return ( +
    + value.replace(/\d/g, "X"), + value: "S1234567D", + }, + { + disableMaskUnmask: true, + displayWidth: "half", + label: "With mask range but disabled unmasking", + maskRange: [1, 4], + maskState: "masked", + value: "S1234567D", + }, + ]} + /> +
    + ); +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/partial-custom-sections.e2e.tsx b/e2e/nextjs-app/src/app/components/uneditable-section/partial-custom-sections.e2e.tsx new file mode 100644 index 0000000000..24e091aa8e --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/partial-custom-sections.e2e.tsx @@ -0,0 +1,47 @@ +"use client"; + +import { UneditableSection } from "@lifesg/react-design-system/uneditable-section"; +import { Alert } from "@lifesg/react-design-system/alert"; +import { Button } from "@lifesg/react-design-system/button"; + +export default function Story() { + return ( + + Custom alert section + + } + items={[ + { + label: "Name (as in NRIC or passport)", + value: "Tom Tan Li Ho", + displayWidth: "half", + }, + { + label: "NRIC or FIN", + value: "S1234534J", + displayWidth: "half", + }, + { + label: "Date of birth", + value: "6 November 1992", + displayWidth: "half", + }, + { + label: "Nationality", + value: "Singaporean", + displayWidth: "half", + }, + ]} + bottomSection={ +
    + +
    + } + /> + ); +} diff --git a/e2e/nextjs-app/src/app/components/uneditable-section/uneditable-section-shared.ts b/e2e/nextjs-app/src/app/components/uneditable-section/uneditable-section-shared.ts new file mode 100644 index 0000000000..1d8304fd43 --- /dev/null +++ b/e2e/nextjs-app/src/app/components/uneditable-section/uneditable-section-shared.ts @@ -0,0 +1,30 @@ +import { UneditableSectionItemProps } from "@lifesg/react-design-system/uneditable-section"; + +export const items: UneditableSectionItemProps[] = [ + { + displayWidth: "half", + label: "Name (as in NRIC or passport)", + value: "Tom Tan Li Ho", + }, + { + displayWidth: "half", + label: "NRIC or FIN", + maskRange: [1, 4], + maskState: "masked", + value: "S1234534J", + }, + { + displayWidth: "half", + label: "Date of birth", + value: "6 November 1992", + }, + { + displayWidth: "half", + label: "Residential Address", + value: "Block 287, #05-11, Tampines Street 22, Singapore 534788", + }, + { + label: "Ethnicity", + value: "Chinese", + }, +]; diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Custom-sections--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Custom-sections--mount.png new file mode 100644 index 0000000000..be15881b1c Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Custom-sections--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default--mount.png new file mode 100644 index 0000000000..388fade6e6 Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-dark-mode---mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-dark-mode---mount.png new file mode 100644 index 0000000000..765078f47b Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-dark-mode---mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-full-width--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-full-width--mount.png new file mode 100644 index 0000000000..e77fa7ed1e Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-full-width--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-mobile--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-mobile--mount.png new file mode 100644 index 0000000000..e2f6ad6adc Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-mobile--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-no-background--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-no-background--mount.png new file mode 100644 index 0000000000..79b72a1c7c Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-no-background--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-no-background-dark-mode---mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-no-background-dark-mode---mount.png new file mode 100644 index 0000000000..0bc30754b6 Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Default-no-background-dark-mode---mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Error-and-alert-variant--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Error-and-alert-variant--mount.png new file mode 100644 index 0000000000..88497356eb Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Error-and-alert-variant--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Error-and-alert-variant-dark-mode---mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Error-and-alert-variant-dark-mode---mount.png new file mode 100644 index 0000000000..5188c6bc8b Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Error-and-alert-variant-dark-mode---mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Full-custom-sections--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Full-custom-sections--mount.png new file mode 100644 index 0000000000..036481629f Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Full-custom-sections--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Loading-variant--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Loading-variant--mount.png new file mode 100644 index 0000000000..e4bcbdfa75 Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Loading-variant--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Loading-variant-dark-mode---mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Loading-variant-dark-mode---mount.png new file mode 100644 index 0000000000..8efe395d6c Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Loading-variant-dark-mode---mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Masked-variants--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Masked-variants--mount.png new file mode 100644 index 0000000000..512806a699 Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Masked-variants--mount.png differ diff --git a/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Partial-custom-sections--mount.png b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Partial-custom-sections--mount.png new file mode 100644 index 0000000000..be15881b1c Binary files /dev/null and b/e2e/tests/components/uneditable-section/__screenshots__/chromium/UneditableSection-Partial-custom-sections--mount.png differ diff --git a/e2e/tests/components/uneditable-section/uneditable-section.e2e.spec.ts b/e2e/tests/components/uneditable-section/uneditable-section.e2e.spec.ts new file mode 100644 index 0000000000..b81c21e812 --- /dev/null +++ b/e2e/tests/components/uneditable-section/uneditable-section.e2e.spec.ts @@ -0,0 +1,149 @@ +import { test as base, Page } from "@playwright/test"; +import { AbstractStoryPage, compareScreenshot } from "../../utils"; + +class StoryPage extends AbstractStoryPage { + protected readonly component = "uneditable-section"; + + constructor(page: Page) { + super(page); + } +} + +const test = base.extend<{ story: StoryPage }>({ + story: async ({ page }, use) => { + const story = new StoryPage(page); + await use(story); + }, +}); + +test.describe("UneditableSection", () => { + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("default-layout"); + }); + + test("Default", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("default-layout", { mode: "dark" }); + }); + + test("Default (dark mode)", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("default-layout", { size: "mobile" }); + }); + + test("Default mobile", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("default-no-background"); + }); + + test("Default no background", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("default-no-background", { mode: "dark" }); + }); + + test("Default no background (dark mode)", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("default-full-width"); + }); + + test("Default full width", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("masked-variants"); + }); + + test("Masked variants", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("loading-variant"); + }); + + test("Loading variant", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("loading-variant", { mode: "dark" }); + }); + + test("Loading variant (dark mode)", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("error-alert-variant"); + }); + + test("Error and alert variant", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("error-alert-variant", { mode: "dark" }); + }); + + test("Error and alert variant (dark mode)", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("partial-custom-sections"); + }); + + test("Partial custom sections", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); + + test.describe(() => { + test.beforeEach(async ({ story }) => { + await story.init("full-custom-sections"); + }); + + test("Full custom sections", async ({ story }) => { + await compareScreenshot(story, "mount"); + }); + }); +}); diff --git a/src/uneditable-section/item-section.tsx b/src/uneditable-section/item-section.tsx index e7eddb436f..9cd93af29c 100644 --- a/src/uneditable-section/item-section.tsx +++ b/src/uneditable-section/item-section.tsx @@ -1,13 +1,24 @@ +import clsx from "clsx"; import React from "react"; import type { UneditableSectionItemSectionProps } from "./types"; -import { GridUl } from "./uneditable-section.styles"; +import * as styles from "./uneditable-section.styles"; export const Component = ( - { stretch, ...otherProps }: UneditableSectionItemSectionProps, + { stretch, className, ...otherProps }: UneditableSectionItemSectionProps, ref: React.Ref ) => { - return ; + return ( +
      + ); }; export const UneditableItemSection = React.forwardRef(Component); diff --git a/src/uneditable-section/section-item.styles.ts b/src/uneditable-section/section-item.styles.ts new file mode 100644 index 0000000000..622311957e --- /dev/null +++ b/src/uneditable-section/section-item.styles.ts @@ -0,0 +1,104 @@ +import { css } from "@linaria/core"; + +import { Colour, Font, MediaQuery } from "../theme"; + +// ============================================================================= +// STYLING +// ============================================================================= +export const container = css` + display: flex; + flex-direction: column; + + &[data-width="half"] { + grid-column: auto / span 4; + } + + &[data-width="half"].containerFullWidth { + grid-column: auto / span 1; + } + + &[data-width="full"] { + grid-column: auto / span 8; + } + + &[data-width="full"].containerFullWidth { + grid-column: auto / span 2; + } + + ${MediaQuery.MaxWidth.lg} { + grid-column: 1 / -1; + } + + overflow-wrap: break-word; +`; + +export const containerFullWidth = css``; + +export const iconContainer = css` + display: flex; + height: 100%; + align-items: center; + justify-content: center; + color: ${Colour["icon-primary"]}; + margin-left: 0.5rem; + + svg { + width: ${Font.Spec["body-size-baseline"]}; + height: ${Font.Spec["body-size-baseline"]}; + } +`; + +export const clickable = css` + ${Font["body-baseline-regular"]} + color: ${Colour["text"]}; + border: none; + background: transparent; + padding: 0; + display: flex; + cursor: pointer; + align-items: center; + overflow-wrap: anywhere; + text-align: left; + + span { + overflow-wrap: anywhere; + text-align: left; + } +`; + +// ----------------------------------------------------------------------------- +// LOADING DISPLAY +// ----------------------------------------------------------------------------- +export const loadingLabel = css` + color: ${Colour["text-disabled"]}; +`; + +export const spinner = css` + margin-right: 0.5rem; + color: ${Colour["text-disabled"]}; +`; + +// ----------------------------------------------------------------------------- +// ERROR DISPLAY +// ----------------------------------------------------------------------------- +export const errorIcon = css` + color: ${Colour["icon-warning"]}; + margin-right: 0.5rem; + height: ${Font.Spec["body-size-baseline"]}; + width: ${Font.Spec["body-size-baseline"]}; +`; + +export const errorLabel = css` + color: ${Colour["text-warning"]}; +`; + +export const tryAgainLabel = css` + ${Font["body-baseline-semibold"]} + color: ${Colour["hyperlink"]}; + text-decoration: underline; + margin-left: 0.5rem; +`; + +export const alert = css` + margin-top: 0.5rem; +`; diff --git a/src/uneditable-section/section-item.styles.tsx b/src/uneditable-section/section-item.styles.tsx deleted file mode 100644 index 52613a7995..0000000000 --- a/src/uneditable-section/section-item.styles.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { ExclamationTriangleIcon } from "@lifesg/react-icons/exclamation-triangle"; -import styled, { css } from "styled-components"; - -import { Alert } from "../alert"; -import { ComponentLoadingSpinner } from "../shared/component-loading-spinner"; -import { V3_Colour, V3_Font, V3_MediaQuery } from "../v3_theme"; -import type { UneditableSectionItemDisplayWidth } from "./types"; - -// ============================================================================= -// STYLING INTERFACES -// ============================================================================= -interface ContainerStyleProps { - $widthStyle: UneditableSectionItemDisplayWidth; - $fullWidth?: boolean | undefined; -} - -// ============================================================================= -// STYLING -// ============================================================================= -export const Container = styled.li` - display: flex; - flex-direction: column; - - ${(props) => { - switch (props.$widthStyle) { - case "half": - return css` - grid-column: auto / span ${props.$fullWidth ? 1 : 4}; - `; - case "full": - return css` - grid-column: auto / span ${props.$fullWidth ? 2 : 8}; - `; - } - }} - - ${V3_MediaQuery.MaxWidth.lg} { - grid-column: 1 / -1; - } - - overflow-wrap: break-word; -`; - -export const IconContainer = styled.div` - display: flex; - height: 100%; - align-items: center; - justify-content: center; - color: ${V3_Colour["icon-primary"]}; - margin-left: 0.5rem; - - svg { - width: ${V3_Font.Spec["body-size-baseline"]}; - height: ${V3_Font.Spec["body-size-baseline"]}; - } -`; - -export const Clickable = styled.button` - ${V3_Font["body-baseline-regular"]} - color: ${V3_Colour["text"]}; - border: none; - background: transparent; - padding: 0; - display: flex; - cursor: pointer; - align-items: center; - overflow-wrap: anywhere; - text-align: left; - - span { - overflow-wrap: anywhere; - text-align: left; - } -`; - -// ----------------------------------------------------------------------------- -// LOADING DISPLAY -// ----------------------------------------------------------------------------- -export const LoadingLabel = styled.span` - color: ${V3_Colour["text-disabled"]}; -`; - -export const Spinner = styled(ComponentLoadingSpinner)` - margin-right: 0.5rem; - color: ${V3_Colour["text-disabled"]}; -`; - -// ----------------------------------------------------------------------------- -// ERROR DISPLAY -// ----------------------------------------------------------------------------- -export const ErrorIcon = styled(ExclamationTriangleIcon)` - color: ${V3_Colour["icon-warning"]}; - margin-right: 0.5rem; - height: ${V3_Font.Spec["body-size-baseline"]}; - width: ${V3_Font.Spec["body-size-baseline"]}; -`; - -export const ErrorLabel = styled.span` - color: ${V3_Colour["text-warning"]}; -`; - -export const TryAgainLabel = styled.span` - ${V3_Font["body-baseline-semibold"]} - color: ${V3_Colour["hyperlink"]}; - text-decoration: underline; - margin-left: 0.5rem; -`; - -export const StyledAlert = styled(Alert)` - margin-top: 0.5rem; -`; diff --git a/src/uneditable-section/section-item.tsx b/src/uneditable-section/section-item.tsx index 1b2c5abba4..c536eca9c1 100644 --- a/src/uneditable-section/section-item.tsx +++ b/src/uneditable-section/section-item.tsx @@ -1,22 +1,16 @@ +import { ExclamationTriangleIcon } from "@lifesg/react-icons/exclamation-triangle"; import { EyeIcon } from "@lifesg/react-icons/eye"; import { EyeSlashIcon } from "@lifesg/react-icons/eye-slash"; +import clsx from "clsx"; import { useEffect, useState } from "react"; +import { Alert } from "../alert"; import { FormLabel } from "../form/form-label"; import { VisuallyHidden } from "../shared/accessibility"; +import { ComponentLoadingSpinner } from "../shared/component-loading-spinner"; import { Typography } from "../typography"; import { StringHelper } from "../util/string-helper"; -import { - Clickable, - Container, - ErrorIcon, - ErrorLabel, - IconContainer, - LoadingLabel, - Spinner, - StyledAlert, - TryAgainLabel, -} from "./section-item.styles"; +import * as styles from "./section-item.styles"; import type { UneditableSectionItemMaskState, UneditableSectionItemProps, @@ -115,26 +109,29 @@ export const UneditableSectionItem = ({ case "fail": return ( <> - - Error - + + Error + Try again? {label} - + ); case "loading": return ( <> - - Retrieving... + + + Retrieving... + ); default: return ( <> {getValue()} - )} - + ); } @@ -166,7 +163,8 @@ export const UneditableSectionItem = ({ } return ( - {renderMaskingState()} - + ); }; return ( - +
    • {label} {renderContent()} - {alert && } - + {alert && ( + + )} +
    • ); }; diff --git a/src/uneditable-section/uneditable-section.styles.ts b/src/uneditable-section/uneditable-section.styles.ts new file mode 100644 index 0000000000..655e23a3c5 --- /dev/null +++ b/src/uneditable-section/uneditable-section.styles.ts @@ -0,0 +1,74 @@ +import { css } from "@linaria/core"; + +import { Colour, MediaQuery } from "../theme"; + +// ============================================================================= +// STYLING +// ============================================================================= +const columnWidthBase = ` + grid-column: span 8; + + ${MediaQuery.MaxWidth.sm} { + grid-column: 1 / -1; + } +`; + +export const columnWidthStretch = css` + grid-column: 1 / -1; +`; + +export const wrapper = css` + background: transparent; + padding-top: 2rem; + padding-bottom: 2rem; +`; + +export const wrapperBackground = css` + background: ${Colour["bg-strong"]}; +`; + +export const fullWidthWrapper = css` + background: transparent; +`; + +export const fullWidthWrapperBackground = css` + background: ${Colour["bg-strong"]}; +`; + +export const title = css` + margin-bottom: 1rem; + ${columnWidthBase} +`; + +export const description = css` + margin-bottom: 2rem; + ${columnWidthBase} +`; + +export const customSection = css` + ${columnWidthBase} +`; + +export const gridUl = css` + ${columnWidthBase} + column-gap: 2rem; + row-gap: 2rem; + display: grid; + grid-template-columns: repeat(8, minmax(0, 1fr)); + + ${MediaQuery.MaxWidth.lg} { + column-gap: 1.5rem; + } + + ${MediaQuery.MaxWidth.sm} { + column-gap: 1rem; + } + + &:not(:last-child) { + margin-bottom: 2rem; + } +`; + +export const gridUlFullWidth = css` + grid-template-columns: repeat(2, minmax(0, 1fr)); +`; diff --git a/src/uneditable-section/uneditable-section.styles.tsx b/src/uneditable-section/uneditable-section.styles.tsx deleted file mode 100644 index 559b409bff..0000000000 --- a/src/uneditable-section/uneditable-section.styles.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import styled, { css } from "styled-components"; - -import { Layout } from "../layout"; -import { Typography } from "../typography"; -import { V3_Colour, V3_MediaQuery } from "../v3_theme"; - -// ============================================================================= -// STYLE INTERFACES -// ============================================================================= -interface WrapperStyleProps { - $background: boolean; -} - -interface ContentStyleProps { - $stretch: boolean | undefined; -} - -interface GridStyleProps extends ContentStyleProps { - $fullWidth?: boolean | undefined; -} -// ============================================================================= -// STYLING -// ============================================================================= -const columnWidthStyle = css` - grid-column: ${(props) => (props.$stretch ? "1 / -1" : "span 8")}; - - ${V3_MediaQuery.MaxWidth.sm} { - grid-column: 1 / -1; - } -`; - -export const Wrapper = styled(Layout.Content)` - background: ${({ $background }) => - $background ? V3_Colour["bg-strong"] : "transparent"}; - padding-top: 2rem; - padding-bottom: 2rem; -`; - -export const FullWidthWrapper = styled.div` - background: ${({ $background }) => - $background ? V3_Colour["bg-strong"] : "transparent"}; -`; - -export const Title = styled(Typography.HeadingSM)` - margin-bottom: 1rem; - ${columnWidthStyle} -`; - -export const Description = styled(Typography.BodyBL)` - margin-bottom: 2rem; - ${columnWidthStyle} -`; - -export const CustomSection = styled.div` - ${columnWidthStyle} -`; - -export const GridUl = styled.ul` - ${columnWidthStyle} - column-gap: 2rem; - row-gap: 2rem; - display: grid; - grid-template-columns: ${({ $fullWidth }) => - $fullWidth ? "repeat(2, minmax(0, 1fr))" : "repeat(8, minmax(0, 1fr))"}; - - ${V3_MediaQuery.MaxWidth.lg} { - column-gap: 1.5rem; - } - - ${V3_MediaQuery.MaxWidth.sm} { - column-gap: 1rem; - } - - &:not(:last-child) { - margin-bottom: 2rem; - } -`; diff --git a/src/uneditable-section/uneditable-section.tsx b/src/uneditable-section/uneditable-section.tsx index b6a0fd4b90..876d039970 100644 --- a/src/uneditable-section/uneditable-section.tsx +++ b/src/uneditable-section/uneditable-section.tsx @@ -1,16 +1,13 @@ +import clsx from "clsx"; + +import { Layout } from "../layout"; +import { Typography } from "../typography"; import { UneditableSectionItem } from "./section-item"; import type { UneditableSectionItemProps, UneditableSectionProps, } from "./types"; -import { - CustomSection, - Description, - FullWidthWrapper, - GridUl, - Title, - Wrapper, -} from "./uneditable-section.styles"; +import * as styles from "./uneditable-section.styles"; export const UneditableSectionBase = ({ items, @@ -25,6 +22,7 @@ export const UneditableSectionBase = ({ onMask, onUnmask, onTryAgain, + className, ...otherProps }: UneditableSectionProps) => { // ============================================================================= @@ -61,9 +59,15 @@ export const UneditableSectionBase = ({ }); return ( - +
        {renderedItems} - +
      ); } @@ -78,27 +82,49 @@ export const UneditableSectionBase = ({ return ( <> {title && ( - {title} - + )} {description && ( - {description} + + {description} + )} {topSection && ( - +
      {topSection} - +
      )} {renderItems()} {bottomSection && ( - +
      {bottomSection} - +
      )} ); @@ -106,14 +132,29 @@ export const UneditableSectionBase = ({ if (fullWidth) { return ( - +
      {renderChildren()} - +
      ); } return ( - + {renderChildren()} - + ); };