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 button
+
+
+
+ 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={
+
+ Custom button
+
+ }
+ />
+ );
+}
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 (
-
+
+
);
}
@@ -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()}
-
+
);
};