diff --git a/frontend/src/metabase/components/DimensionInfo/DimensionInfo.styled.jsx b/frontend/src/metabase/components/DimensionInfo/DimensionInfo.styled.jsx deleted file mode 100644 index 78b24bdb7dd7c16375beacc2575eddb353d3dd1f..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/components/DimensionInfo/DimensionInfo.styled.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import styled from "styled-components"; - -import { color } from "metabase/lib/colors"; -import { space } from "metabase/styled-components/theme"; - -export const Container = styled.div` - display: flex; - flex-direction: column; - gap: ${space(2)}; - padding: ${space(2)}; - overflow: auto; -`; - -export const Description = styled.div` - font-size: 14px; - white-space: pre-line; - max-height: 200px; - overflow: auto; -`; - -export const EmptyDescription = styled(Description)` - color: ${color("text-light")}; - font-weight: 700; -`; diff --git a/frontend/src/metabase/components/DimensionLabel/DimensionLabel.styled.jsx b/frontend/src/metabase/components/DimensionLabel/DimensionLabel.styled.jsx deleted file mode 100644 index 485249abebbc84df61b24f3ba865d8cae5976a57..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/components/DimensionLabel/DimensionLabel.styled.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import styled from "styled-components"; - -import { space } from "metabase/styled-components/theme"; -import { color } from "metabase/lib/colors"; -import Icon from "metabase/components/Icon"; - -export const Container = styled.div` - display: inline-flex; - align-items: center; - column-gap: ${space(0)}; - font-size: 12px; -`; - -export const Label = styled.span` - font-weight: 900; - color: ${color("brand")}; - font-size: 1em; -`; - -export const PaddedInvertedColorIcon = styled(Icon)` - background-color: ${color("brand")}; - color: ${color("white")}; - border-radius: ${space(0)}; - padding: ${space(0)}; - height: 1em; - width: 1em; -`; diff --git a/frontend/src/metabase/components/DimensionInfo/DimensionInfo.info.js b/frontend/src/metabase/components/MetadataInfo/DimensionInfo/DimensionInfo.info.js similarity index 100% rename from frontend/src/metabase/components/DimensionInfo/DimensionInfo.info.js rename to frontend/src/metabase/components/MetadataInfo/DimensionInfo/DimensionInfo.info.js diff --git a/frontend/src/metabase/components/DimensionInfo/DimensionInfo.jsx b/frontend/src/metabase/components/MetadataInfo/DimensionInfo/DimensionInfo.jsx similarity index 78% rename from frontend/src/metabase/components/DimensionInfo/DimensionInfo.jsx rename to frontend/src/metabase/components/MetadataInfo/DimensionInfo/DimensionInfo.jsx index ff85b731c4c10b843b7f56a5c97eacbbfc8b8620..e66827372f4fd05722ffee79bb4c7f025234fc71 100644 --- a/frontend/src/metabase/components/DimensionInfo/DimensionInfo.jsx +++ b/frontend/src/metabase/components/MetadataInfo/DimensionInfo/DimensionInfo.jsx @@ -3,13 +3,13 @@ import PropTypes from "prop-types"; import { t } from "ttag"; import Dimension from "metabase-lib/lib/Dimension"; -import DimensionLabel from "metabase/components/DimensionLabel"; +import DimensionLabel from "metabase/components/MetadataInfo/DimensionLabel"; import { - Container, + InfoContainer, Description, EmptyDescription, -} from "./DimensionInfo.styled"; +} from "../MetadataInfo.styled"; DimensionInfo.propTypes = { className: PropTypes.string, @@ -20,14 +20,14 @@ function DimensionInfo({ className, dimension }) { const field = dimension.field(); const description = field?.description; return ( - <Container className={className}> + <InfoContainer className={className}> {description ? ( <Description>{description}</Description> ) : ( <EmptyDescription>{t`No description`}</EmptyDescription> )} <DimensionLabel dimension={dimension} /> - </Container> + </InfoContainer> ); } diff --git a/frontend/src/metabase/components/DimensionInfo/DimensionInfo.unit.spec.js b/frontend/src/metabase/components/MetadataInfo/DimensionInfo/DimensionInfo.unit.spec.js similarity index 100% rename from frontend/src/metabase/components/DimensionInfo/DimensionInfo.unit.spec.js rename to frontend/src/metabase/components/MetadataInfo/DimensionInfo/DimensionInfo.unit.spec.js diff --git a/frontend/src/metabase/components/DimensionInfo/index.js b/frontend/src/metabase/components/MetadataInfo/DimensionInfo/index.js similarity index 100% rename from frontend/src/metabase/components/DimensionInfo/index.js rename to frontend/src/metabase/components/MetadataInfo/DimensionInfo/index.js diff --git a/frontend/src/metabase/components/DimensionLabel/DimensionLabel.info.js b/frontend/src/metabase/components/MetadataInfo/DimensionLabel/DimensionLabel.info.js similarity index 100% rename from frontend/src/metabase/components/DimensionLabel/DimensionLabel.info.js rename to frontend/src/metabase/components/MetadataInfo/DimensionLabel/DimensionLabel.info.js diff --git a/frontend/src/metabase/components/DimensionLabel/DimensionLabel.jsx b/frontend/src/metabase/components/MetadataInfo/DimensionLabel/DimensionLabel.jsx similarity index 64% rename from frontend/src/metabase/components/DimensionLabel/DimensionLabel.jsx rename to frontend/src/metabase/components/MetadataInfo/DimensionLabel/DimensionLabel.jsx index 984fc934727a6722d2cc85f4da6f09b97507dc42..7bf9d86927011a73de69b15b6a7490adb29536a3 100644 --- a/frontend/src/metabase/components/DimensionLabel/DimensionLabel.jsx +++ b/frontend/src/metabase/components/MetadataInfo/DimensionLabel/DimensionLabel.jsx @@ -4,10 +4,10 @@ import PropTypes from "prop-types"; import Dimension from "metabase-lib/lib/Dimension"; import { - Container, + LabelContainer, Label, - PaddedInvertedColorIcon, -} from "./DimensionLabel.styled"; + InvertedColorRelativeSizeIcon, +} from "../MetadataInfo.styled"; DimensionLabel.propTypes = { className: PropTypes.string, @@ -16,9 +16,9 @@ DimensionLabel.propTypes = { export default function DimensionLabel({ className, dimension }) { return ( - <Container className={className}> - <PaddedInvertedColorIcon name={dimension.icon()} /> + <LabelContainer className={className}> + <InvertedColorRelativeSizeIcon name={dimension.icon()} /> <Label>{dimension.displayName()}</Label> - </Container> + </LabelContainer> ); } diff --git a/frontend/src/metabase/components/DimensionLabel/DimensionLabel.unit.spec.js b/frontend/src/metabase/components/MetadataInfo/DimensionLabel/DimensionLabel.unit.spec.js similarity index 100% rename from frontend/src/metabase/components/DimensionLabel/DimensionLabel.unit.spec.js rename to frontend/src/metabase/components/MetadataInfo/DimensionLabel/DimensionLabel.unit.spec.js diff --git a/frontend/src/metabase/components/DimensionLabel/index.js b/frontend/src/metabase/components/MetadataInfo/DimensionLabel/index.js similarity index 100% rename from frontend/src/metabase/components/DimensionLabel/index.js rename to frontend/src/metabase/components/MetadataInfo/DimensionLabel/index.js diff --git a/frontend/src/metabase/components/MetadataInfo/MetadataInfo.styled.jsx b/frontend/src/metabase/components/MetadataInfo/MetadataInfo.styled.jsx new file mode 100644 index 0000000000000000000000000000000000000000..078f78e946f2cb6a190c489612098d1a58b12d78 --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/MetadataInfo.styled.jsx @@ -0,0 +1,51 @@ +import styled from "styled-components"; + +import { space } from "metabase/styled-components/theme"; +import { color } from "metabase/lib/colors"; +import Icon from "metabase/components/Icon"; + +export const InfoContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${space(2)}; + padding: ${space(2)}; + overflow: auto; +`; + +export const Description = styled.div` + font-size: 14px; + white-space: pre-line; + max-height: 200px; + overflow: auto; +`; + +export const EmptyDescription = styled(Description)` + color: ${color("text-light")}; + font-weight: 700; +`; + +export const LabelContainer = styled.div` + display: inline-flex; + align-items: center; + column-gap: ${space(0)}; + font-size: 12px; + color: ${({ color: _color = "brand" }) => color(_color)}; +`; + +export const Label = styled.span` + font-weight: 900; + font-size: 1em; +`; + +export const RelativeSizeIcon = styled(Icon)` + height: 1em; + width: 1em; +`; + +export const InvertedColorRelativeSizeIcon = styled(RelativeSizeIcon)` + padding: ${space(0)}; + background-color: ${color("brand")}; + color: ${color("white")}; + border-radius: ${space(0)}; + padding: ${space(0)}; +`; diff --git a/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.info.js b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.info.js new file mode 100644 index 0000000000000000000000000000000000000000..c0dc9412a222748bd152f7c13176a074ba311cfe --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.info.js @@ -0,0 +1,30 @@ +import React from "react"; + +import { PRODUCTS } from "__support__/sample_dataset_fixture"; +import Card from "metabase/components/Card"; +import PopoverWithTrigger from "metabase/components/PopoverWithTrigger"; +import Button from "metabase/components/Button"; +import Table from "metabase-lib/lib/metadata/Table"; + +import TableInfo from "./TableInfo"; + +const table = new Table(PRODUCTS); +const tableNoDescription = new Table({ id: 123, display_name: "Foo" }); + +export const component = TableInfo; +export const description = + "A selection of information from a given Table instance, for use in some containing component"; +export const examples = { + "with description": <TableInfo table={table} />, + "without description": <TableInfo table={tableNoDescription} />, + "in a card": ( + <Card> + <TableInfo table={table} /> + </Card> + ), + "in a popoover": ( + <PopoverWithTrigger triggerElement={<Button>click me</Button>}> + <TableInfo table={table} /> + </PopoverWithTrigger> + ), +}; diff --git a/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.jsx b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.jsx new file mode 100644 index 0000000000000000000000000000000000000000..2eedb63fcf0b554fa529aaaa399fda0039e1073f --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.jsx @@ -0,0 +1,34 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { t } from "ttag"; + +import Table from "metabase-lib/lib/metadata/Table"; +import TableLabel from "metabase/components/MetadataInfo/TableLabel"; + +import { + InfoContainer, + Description, + EmptyDescription, +} from "../MetadataInfo.styled"; + +TableInfo.propTypes = { + className: PropTypes.string, + table: PropTypes.instanceOf(Table).isRequired, +}; + +function TableInfo({ className, table }) { + const description = table.description; + + return ( + <InfoContainer className={className}> + {description ? ( + <Description>{description}</Description> + ) : ( + <EmptyDescription>{t`No description`}</EmptyDescription> + )} + <TableLabel table={table} /> + </InfoContainer> + ); +} + +export default TableInfo; diff --git a/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.unit.spec.js b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.unit.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..4ebfe45276d323f0cd0db00baf0e20fcedccafb7 --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.unit.spec.js @@ -0,0 +1,30 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; + +import { PRODUCTS } from "__support__/sample_dataset_fixture"; +import Table from "metabase-lib/lib/metadata/Table"; + +import TableInfo from "./TableInfo"; + +const table = new Table(PRODUCTS); +const tableNoDescription = new Table({ id: 123, display_name: "Foo" }); + +describe("TableInfo", () => { + beforeEach(() => { + render(<TableInfo table={table} />); + }); + + it("should display the given table's name", () => { + expect(screen.getByText(PRODUCTS.display_name)).toBeInTheDocument(); + }); + + it("should display the given table's description", () => { + expect(screen.getByText(PRODUCTS.description)).toBeInTheDocument(); + }); + + it("should display a placeholder if table has no description", () => { + render(<TableInfo table={tableNoDescription} />); + + expect(screen.getByText("No description")).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/metabase/components/MetadataInfo/TableInfo/index.js b/frontend/src/metabase/components/MetadataInfo/TableInfo/index.js new file mode 100644 index 0000000000000000000000000000000000000000..a14e36ecf425c48e9438c06439028583ab787fe7 --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/TableInfo/index.js @@ -0,0 +1 @@ +export { default } from "./TableInfo"; diff --git a/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.info.js b/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.info.js new file mode 100644 index 0000000000000000000000000000000000000000..e7056931c2e6903a29cd4cb4d1d49eebe589b572 --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.info.js @@ -0,0 +1,21 @@ +import React from "react"; +import styled from "styled-components"; + +import { PRODUCTS } from "__support__/sample_dataset_fixture"; +import Table from "metabase-lib/lib/metadata/Table"; + +import TableLabel from "./TableLabel"; + +const table = new Table({ + ...PRODUCTS, +}); +const BiggerTableLabel = styled(TableLabel)` + font-size: 32px; +`; + +export const component = TableLabel; +export const description = "A label for instances of Table"; +export const examples = { + TableLabel: <TableLabel table={table} />, + "Bigger TableLabel": <BiggerTableLabel table={table} />, +}; diff --git a/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.jsx b/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.jsx new file mode 100644 index 0000000000000000000000000000000000000000..d1353a5e4e42836cd953b6c54c59acc7a7289c79 --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import PropTypes from "prop-types"; + +import Table from "metabase-lib/lib/metadata/Table"; +import { + LabelContainer, + Label, + RelativeSizeIcon, +} from "../MetadataInfo.styled"; + +const propTypes = { + className: PropTypes.string, + table: PropTypes.instanceOf(Table).isRequired, +}; + +function TableLabel({ className, table }) { + return ( + <LabelContainer className={className}> + <RelativeSizeIcon name="table" /> + <Label>{table.displayName()}</Label> + </LabelContainer> + ); +} + +TableLabel.propTypes = propTypes; + +export default TableLabel; diff --git a/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.unit.spec.js b/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.unit.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..2da22a9744f990892d9b3f0a043f01788355da3f --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/TableLabel/TableLabel.unit.spec.js @@ -0,0 +1,15 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; + +import Table from "metabase-lib/lib/metadata/Table"; +import TableLabel from "./TableLabel"; + +describe("TableLabel", () => { + beforeEach(() => { + render(<TableLabel table={new Table({ id: 1, display_name: "Foo" })} />); + }); + + it("should display the given table's display name", () => { + expect(screen.getByText("Foo")).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/metabase/components/MetadataInfo/TableLabel/index.js b/frontend/src/metabase/components/MetadataInfo/TableLabel/index.js new file mode 100644 index 0000000000000000000000000000000000000000..8253b659257aa23bd52cb6be55d17f7d49aab09f --- /dev/null +++ b/frontend/src/metabase/components/MetadataInfo/TableLabel/index.js @@ -0,0 +1 @@ +export { default } from "./TableLabel";