Repository: GoogleChromeLabs/ps-analysis-tool
Pull request #94 (2023-08-01 18:24:57)

Feature: Cookie Landing page - Part -1
## Description

Introduce `design-system` directory for common components, theme, etc.

## Relevant Technical Choices
- Refactor code to move some components used in Popup to design-system
- Add tests coverage
- Create stories.
- Rearrange stories in the storybook.


## Testing Instructions

<!--
How can the changes in this PR be verified?
Please provide step-by-step instructions to test the changes.
-->

## Additional Information:

<!-- Any other information. -->

## Screenshot/Screencast

<!-- Please provide Screenshot/Screencast, if applicable -->

---

<!--
Example:

Fixes #123
See #834
-->

Partially addresses https://github.com/GoogleChromeLabs/ps-analysis-tool/issues/66


Patch:
diff --git a/.eslintrc.json b/.eslintrc.json
index caf0b8d6..d8d63740 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -32,7 +32,13 @@
       }
     }
   },
-  "ignorePatterns": ["**/node_modules/**", "dist/**", "out/**", "data/"],
+  "ignorePatterns": [
+    "**/node_modules/**",
+    "dist/**",
+    "out/**",
+    "data/**",
+    "coverage/**"
+  ],
   "rules": {
     "array-callback-return": "error",
     "block-scoped-var": "error",
diff --git a/packages/extension/src/view/popup/components/circle.tsx b/packages/extension/src/view/design-system/components/circle/index.tsx
similarity index 100%
rename from packages/extension/src/view/popup/components/circle.tsx
rename to packages/extension/src/view/design-system/components/circle/index.tsx
diff --git a/packages/extension/src/view/design-system/components/circle/stories/circle.stories.tsx b/packages/extension/src/view/design-system/components/circle/stories/circle.stories.tsx
new file mode 100644
index 00000000..e424fa43
--- /dev/null
+++ b/packages/extension/src/view/design-system/components/circle/stories/circle.stories.tsx
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed 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
+ *
+ *     https://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.
+ */
+/**
+ * External dependencies.
+ */
+import type { Meta, StoryObj } from '@storybook/react';
+
+/**
+ * Internal dependencies.
+ */
+import Circle from '..';
+import { COLOR_MAP } from '../../../theme/colors';
+
+const meta: Meta<typeof Circle> = {
+  title: 'Extension/DesignSystem/Circle',
+  component: Circle,
+  tags: ['autodocs'],
+};
+
+export default meta;
+
+export const Primary: StoryObj<typeof meta> = {
+  args: {
+    color: COLOR_MAP.functional,
+  },
+};
diff --git a/packages/extension/src/view/popup/components/pieChart.tsx b/packages/extension/src/view/design-system/components/circlePieChart/index.tsx
similarity index 87%
rename from packages/extension/src/view/popup/components/pieChart.tsx
rename to packages/extension/src/view/design-system/components/circlePieChart/index.tsx
index 2893a5ee..d7e2aaef 100644
--- a/packages/extension/src/view/popup/components/pieChart.tsx
+++ b/packages/extension/src/view/design-system/components/circlePieChart/index.tsx
@@ -19,13 +19,13 @@
 import React from 'react';
 import { VictoryPie } from 'victory';
 
-interface PieChartProps {
+interface CirclePieChartProps {
   centerCount: number;
   data: { count: number; color: string }[];
 }
-const PieChart = ({ centerCount, data }: PieChartProps) => {
+const CirclePieChart = ({ centerCount, data }: CirclePieChartProps) => {
   return (
-    <div className="w-full h-full">
+    <div className="w-full h-full max-w-xs">
       <div className="w-full h-full relative">
         <VictoryPie
           padding={0}
@@ -43,4 +43,4 @@ const PieChart = ({ centerCount, data }: PieChartProps) => {
   );
 };
 
-export default PieChart;
+export default CirclePieChart;
diff --git a/packages/extension/src/view/design-system/components/circlePieChart/stories/circlePieChart.stories.tsx b/packages/extension/src/view/design-system/components/circlePieChart/stories/circlePieChart.stories.tsx
new file mode 100644
index 00000000..2e9f3b8d
--- /dev/null
+++ b/packages/extension/src/view/design-system/components/circlePieChart/stories/circlePieChart.stories.tsx
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed 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
+ *
+ *     https://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.
+ */
+/**
+ * External dependencies.
+ */
+import type { Meta, StoryObj } from '@storybook/react';
+
+/**
+ * Internal dependencies.
+ */
+import CirclePieChart from '..';
+import { COLOR_MAP } from '../../../theme/colors';
+
+const meta: Meta<typeof CirclePieChart> = {
+  title: 'Extension/DesignSystem/CirclePieChart',
+  component: CirclePieChart,
+  tags: ['autodocs'],
+};
+
+export default meta;
+
+export const Primary: StoryObj<typeof meta> = {
+  args: {
+    centerCount: 5,
+    data: [
+      {
+        count: 5,
+        color: COLOR_MAP.functional,
+      },
+      {
+        count: 4,
+        color: COLOR_MAP.marketing,
+      },
+      {
+        count: 0,
+        color: COLOR_MAP.analytics,
+      },
+      {
+        count: 9,
+        color: COLOR_MAP.uncategorised,
+      },
+    ],
+  },
+};
diff --git a/packages/extension/src/view/design-system/components/index.ts b/packages/extension/src/view/design-system/components/index.ts
new file mode 100644
index 00000000..24352f12
--- /dev/null
+++ b/packages/extension/src/view/design-system/components/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed 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
+ *
+ *     https://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.
+ */
+export { default as CirclePieChart } from './circlePieChart';
+export { default as Circle } from './circle';
diff --git a/packages/extension/src/view/popup/const.ts b/packages/extension/src/view/design-system/theme/colors.ts
similarity index 100%
rename from packages/extension/src/view/popup/const.ts
rename to packages/extension/src/view/design-system/theme/colors.ts
diff --git a/packages/extension/src/view/devtools/components/sidebar/accordion/stories/accordion.stories.ts b/packages/extension/src/view/devtools/components/sidebar/accordion/stories/accordion.stories.ts
index 0b82ae6e..a141576b 100644
--- a/packages/extension/src/view/devtools/components/sidebar/accordion/stories/accordion.stories.ts
+++ b/packages/extension/src/view/devtools/components/sidebar/accordion/stories/accordion.stories.ts
@@ -26,7 +26,7 @@ import Accordion from '..';
 // @todo To be removed after we add any story.
 // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
 const meta: Meta<typeof Accordion> = {
-  title: 'Extension/Accordion',
+  title: 'Extension/DevTools/Accordion',
   component: Accordion,
   tags: ['autodocs'],
   argTypes: {
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails/stories/cookieDetails.stories.ts b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails/stories/cookieDetails.stories.ts
index 56621591..267bd347 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails/stories/cookieDetails.stories.ts
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails/stories/cookieDetails.stories.ts
@@ -25,7 +25,7 @@ import type { Meta, StoryObj } from '@storybook/react';
 import { CookieDetails } from '../..';
 
 const meta = {
-  title: 'Extension/CookiesPanel/CookieDetails',
+  title: 'Extension/DevTools/CookiesPanel/CookieDetails',
   component: CookieDetails,
   tags: ['autodocs'],
 } as Meta<typeof CookieDetails>;
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails/stories/details.stories.ts b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails/stories/details.stories.ts
index 9d2baf9a..832a8a23 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails/stories/details.stories.ts
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieDetails/stories/details.stories.ts
@@ -25,7 +25,7 @@ import Details from '../details';
 import type { CookieTableData } from '../../../../../../stateProviders/syncCookieStore';
 
 const meta = {
-  title: 'Extension/CookiesPanel/CookieDetails/Details',
+  title: 'Extension/DevTools/CookiesPanel/CookieDetails/Details',
   component: Details,
   tags: ['autodocs'],
 } as Meta<typeof Details>;
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieTable/stories/cookieTable.stories.ts b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieTable/stories/cookieTable.stories.ts
index 504bfc1d..0de42e16 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieTable/stories/cookieTable.stories.ts
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieTable/stories/cookieTable.stories.ts
@@ -26,7 +26,7 @@ import { TempCookieData } from './tempData';
 import { CookieTable } from '../..';
 
 const meta = {
-  title: 'Extension/CookiesPanel/CookieTable',
+  title: 'Extension/DevTools/CookiesPanel/CookieTable',
   component: CookieTable,
   tags: ['autodocs'],
 } as Meta<typeof CookieTable>;
diff --git a/packages/extension/src/view/popup/app.tsx b/packages/extension/src/view/popup/app.tsx
index 44d11409..75e0d8e4 100644
--- a/packages/extension/src/view/popup/app.tsx
+++ b/packages/extension/src/view/popup/app.tsx
@@ -23,9 +23,10 @@ import React from 'react';
  * Internal dependencies.
  */
 import './app.css';
-import { PieChart, Legend } from './components';
+import { Legend } from './components';
 import { useCookieStore } from './stateProviders/syncCookieStore';
-import { COLOR_MAP } from './const';
+import { COLOR_MAP } from '../design-system/theme/colors';
+import { CirclePieChart } from '../design-system/components';
 
 const App: React.FC = () => {
   const { cookieStats } = useCookieStore(({ state }) => ({
@@ -124,7 +125,7 @@ const App: React.FC = () => {
         <div className="w-full h-full flex flex-col justify-center items-center">
           <div className="flex-1 w-full">
             {cookieStats.firstParty.total ? (
-              <PieChart
+              <CirclePieChart
                 centerCount={cookieStats.firstParty.total}
                 data={firstPartyPiechartData}
               />
@@ -141,7 +142,7 @@ const App: React.FC = () => {
         <div className="w-full h-full flex flex-col justify-center items-center">
           <div className="flex-1 w-full h-full">
             {cookieStats.thirdParty.total ? (
-              <PieChart
+              <CirclePieChart
                 centerCount={cookieStats.thirdParty.total}
                 data={thirdPartyPiechartData}
               />
diff --git a/packages/extension/src/view/popup/components/index.tsx b/packages/extension/src/view/popup/components/index.tsx
index 45c9a6b2..82dddbfb 100644
--- a/packages/extension/src/view/popup/components/index.tsx
+++ b/packages/extension/src/view/popup/components/index.tsx
@@ -13,6 +13,4 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-export { default as PieChart } from './pieChart';
 export { default as Legend } from './legend';
diff --git a/packages/extension/src/view/popup/components/legend.tsx b/packages/extension/src/view/popup/components/legend/index.tsx
similarity index 95%
rename from packages/extension/src/view/popup/components/legend.tsx
rename to packages/extension/src/view/popup/components/legend/index.tsx
index 5d4adf26..65b8cd95 100644
--- a/packages/extension/src/view/popup/components/legend.tsx
+++ b/packages/extension/src/view/popup/components/legend/index.tsx
@@ -21,7 +21,7 @@ import React from 'react';
 /**
  * Internal dependencies.
  */
-import Circle from './circle';
+import { Circle } from '../../../design-system/components';
 
 interface LegendProps {
   legendItemList: { label: string; count: number; color: string }[];
diff --git a/packages/extension/src/view/popup/components/legend/stories/legend.stories.tsx b/packages/extension/src/view/popup/components/legend/stories/legend.stories.tsx
new file mode 100644
index 00000000..458948ce
--- /dev/null
+++ b/packages/extension/src/view/popup/components/legend/stories/legend.stories.tsx
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed 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
+ *
+ *     https://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.
+ */
+/**
+ * External dependencies.
+ */
+import type { Meta, StoryObj } from '@storybook/react';
+
+/**
+ * Internal dependencies.
+ */
+import Legend from '..';
+import { COLOR_MAP } from '../../../../design-system/theme/colors';
+
+const meta: Meta<typeof Legend> = {
+  title: 'Extension/Popup/Legend',
+  component: Legend,
+  tags: ['autodocs'],
+};
+
+export default meta;
+
+export const Primary: StoryObj<typeof meta> = {
+  args: {
+    legendItemList: [
+      { label: 'Functional', count: 10, color: COLOR_MAP.functional },
+      { label: 'Marketing', count: 20, color: COLOR_MAP.marketing },
+      { label: 'Analytics', count: 22, color: COLOR_MAP.analytics },
+      { label: 'Uncategorised', count: 11, color: COLOR_MAP.uncategorised },
+    ],
+  },
+};


Test patch:
diff --git a/packages/extension/src/view/design-system/components/circle/tests/circle.tsx b/packages/extension/src/view/design-system/components/circle/tests/circle.tsx
new file mode 100644
index 00000000..d828687b
--- /dev/null
+++ b/packages/extension/src/view/design-system/components/circle/tests/circle.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed 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
+ *
+ *     https://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 React from 'react';
+import { render } from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+
+/**
+ * Internal dependencies.
+ */
+import Circle from '..';
+import { COLOR_MAP } from '../../../theme/colors';
+
+describe('Circle', () => {
+  it('renders the Circle with the correct background color', () => {
+    const { container } = render(<Circle color={COLOR_MAP.functional} />);
+
+    // Check if the Circle div has the correct background color style
+    const circleDiv = container.querySelector('.rounded-full');
+    expect(circleDiv).toHaveStyle(`background-color: ${COLOR_MAP.functional}`);
+  });
+});
diff --git a/packages/extension/src/view/design-system/components/circlePieChart/tests/circlePieChart.tsx b/packages/extension/src/view/design-system/components/circlePieChart/tests/circlePieChart.tsx
new file mode 100644
index 00000000..c87df7e8
--- /dev/null
+++ b/packages/extension/src/view/design-system/components/circlePieChart/tests/circlePieChart.tsx
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed 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
+ *
+ *     https://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 React from 'react';
+import { render } from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+
+/**
+ * Internal dependencies.
+ */
+import CirclePieChart from '..';
+
+describe('CirclePieChart', () => {
+  const testData = [
+    { count: 25, color: 'red' },
+    { count: 50, color: 'blue' },
+    { count: 75, color: 'green' },
+  ];
+  const centerCount = 150;
+
+  it('renders the VictoryPie chart with the correct data', () => {
+    const { container } = render(
+      <CirclePieChart centerCount={centerCount} data={testData} />
+    );
+
+    // Check if the VictoryPie is rendered with the correct data
+    const paths = container.querySelectorAll('path[role="presentation"]');
+    expect(paths.length).toBe(testData.length);
+  });
+
+  it('renders the centerCount text', () => {
+    const { getByText } = render(
+      <CirclePieChart centerCount={centerCount} data={testData} />
+    );
+
+    // Check if the centerCount text is rendered
+    const text: string = centerCount.toString();
+    const centerCountElement = getByText(text);
+    expect(centerCountElement).toBeInTheDocument();
+  });
+});
diff --git a/packages/extension/src/view/popup/components/legend/tests/legend.tsx b/packages/extension/src/view/popup/components/legend/tests/legend.tsx
new file mode 100644
index 00000000..0db602e9
--- /dev/null
+++ b/packages/extension/src/view/popup/components/legend/tests/legend.tsx
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed 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
+ *
+ *     https://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.
+ */
+/**
+ * External dependencies.
+ */
+import React from 'react';
+import { render } from '@testing-library/react';
+import '@testing-library/jest-dom/extend-expect';
+
+/**
+ * Internal dependencies.
+ */
+import Legend from '..';
+
+describe('Legend', () => {
+  const testData = [
+    { label: 'Functional', count: 10, color: 'red' },
+    { label: 'Marketing', count: 20, color: 'blue' },
+  ];
+
+  it('renders the correct label and count for each legend item', () => {
+    const { getAllByText } = render(<Legend legendItemList={testData} />);
+
+    // Check if the correct label and count elements are rendered for each legend item
+    testData.forEach(({ label, count }) => {
+      const labelElement = getAllByText(label);
+      expect(labelElement.length).toBe(1); // Ensure there's only one element with the label text
+
+      const countElement = getAllByText(count.toString());
+      expect(countElement.length).toBe(1); // Ensure there's only one element with the count text
+    });
+  });
+});