diff --git a/gradle/changelog/data_page_template.yaml b/gradle/changelog/data_page_template.yaml new file mode 100644 index 0000000000..89f3645629 --- /dev/null +++ b/gradle/changelog/data_page_template.yaml @@ -0,0 +1,2 @@ +- type: added + description: Atomic design page template simple data pages diff --git a/scm-ui/ui-components/src/Notification.tsx b/scm-ui/ui-components/src/Notification.tsx index 9edfd70dd6..9a97770ea8 100644 --- a/scm-ui/ui-components/src/Notification.tsx +++ b/scm-ui/ui-components/src/Notification.tsx @@ -27,7 +27,7 @@ import classNames from "classnames"; type NotificationType = "primary" | "info" | "success" | "warning" | "danger" | "inherit"; type Props = { - type: NotificationType; + type?: NotificationType; onClose?: () => void; className?: string; children?: ReactNode; diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 40974fa04d..8df6fff880 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -16928,41 +16928,33 @@ exports[`Storyshots Modal/ConfirmAlert Autofocus 1`] = ` Array [ + + )} + + + + + users} action={ellipsis-v}> + + + + We can also enter insane text without whitespace + ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss + + + + + SCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCM + + + SCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCM + + + + + SCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCM + 7/3 + + + {({ labelId }) => ( + <> + SCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCMSCM + + + )} + + + + + + ); + } + + return ( + <> + My Page + + + + + {({ formFieldId }) => ( + <> + Filter by + + + + + )} + + + Create New Data + + {content} + + ); +}; +Example.args = { + error: undefined, + isLoading: false, + isEmpty: false, +}; diff --git a/scm-ui/ui-layout/src/templates/data-page/DataPageHeader.tsx b/scm-ui/ui-layout/src/templates/data-page/DataPageHeader.tsx new file mode 100644 index 0000000000..dcd87900b0 --- /dev/null +++ b/scm-ui/ui-layout/src/templates/data-page/DataPageHeader.tsx @@ -0,0 +1,100 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import withClasses from "../../_helpers/with-classes"; +import React, { HTMLAttributes } from "react"; +import classNames from "classnames"; +import { useGeneratedId } from "@scm-manager/ui-components"; +import { LinkButton } from "@scm-manager/ui-buttons"; + +/** + * @beta + * @since 2.47.0 + */ +export const DataPageHeader = withClasses("div", [ + "is-flex", + "is-flex-wrap-wrap", + "is-justify-content-space-between", + "mb-3", + "has-row-gap-2", + "has-column-gap-4", +]); + +/** + * @beta + * @since 2.47.0 + */ +export const DataPageHeaderSettings = withClasses("div", [ + "is-flex", + "is-flex-wrap-wrap", + "is-align-items-center", + "has-row-gap-2", + "has-column-gap-4", + "is-flex-grow-1", + "is-flex-shrink-1", + "is-flex-basis-0", +]); + +type DataPageHeaderSettingProps = HTMLAttributes & { + children?: React.ReactNode | ((props: { formFieldId: string }) => React.ReactNode); +}; + +/** + * @beta + * @since 2.47.0 + */ +export const DataPageHeaderSetting = React.forwardRef( + ({ className, children, ...props }, ref) => { + const formFieldId = useGeneratedId(); + return ( + + {typeof children === "function" ? children({ formFieldId }) : children} + + ); + } +); + +/** + * @beta + * @since 2.47.0 + */ +export const DataPageHeaderSettingLabel = withClasses("label", [ + "is-flex", + "is-align-items-center", + "is-text-wrap-no-wrap", +]); + +/** + * @beta + * @since 2.47.0 + */ +export const DataPageHeaderSettingField = React.Fragment; + +/** + * @beta + * @since 2.47.0 + */ +export const DataPageHeaderCreateButton = withClasses(LinkButton, ["is-flex-grow-0", "is-flex-shrink-0"], { + variant: "primary", +}); diff --git a/scm-ui/ui-styles/src/components/_flex.scss b/scm-ui/ui-styles/src/components/_flex.scss new file mode 100644 index 0000000000..4985c739cc --- /dev/null +++ b/scm-ui/ui-styles/src/components/_flex.scss @@ -0,0 +1,29 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@each $size, $value in $spacing-values { + .is-flex-basis-#{$size} { + flex-basis: $value; + } +} diff --git a/scm-ui/ui-styles/src/components/_gap.scss b/scm-ui/ui-styles/src/components/_gap.scss new file mode 100644 index 0000000000..3d722a311f --- /dev/null +++ b/scm-ui/ui-styles/src/components/_gap.scss @@ -0,0 +1,35 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@each $size, $value in $spacing-values { + .has-gap-#{$size} { + gap: $value; + } + .has-row-gap-#{$size} { + row-gap: $value; + } + .has-column-gap-#{$size} { + column-gap: $value; + } +} diff --git a/scm-ui/ui-styles/src/components/_main.scss b/scm-ui/ui-styles/src/components/_main.scss index 351b4bfa63..a62a03bdb2 100644 --- a/scm-ui/ui-styles/src/components/_main.scss +++ b/scm-ui/ui-styles/src/components/_main.scss @@ -53,7 +53,13 @@ } } +.is-overflow-wrap-anywhere { + overflow-wrap: anywhere; +} +.is-text-wrap-no-wrap { + text-wrap: nowrap; +} .is-absolute { position: absolute; diff --git a/scm-ui/ui-styles/src/utils/_post.scss b/scm-ui/ui-styles/src/utils/_post.scss index f132a914c6..42d1d2cb45 100644 --- a/scm-ui/ui-styles/src/utils/_post.scss +++ b/scm-ui/ui-styles/src/utils/_post.scss @@ -25,5 +25,7 @@ @import "../variables/_derived.scss"; @import "bulma-popover/css/bulma-popover"; @import "../components/_main.scss"; +@import "../components/_gap.scss"; +@import "../components/_flex.scss"; @import "../components/_tooltip.scss"; @import "../components/_card.scss"; diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/BranchTableWrapper.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/BranchTableWrapper.tsx index f3ea6683eb..428d6832b0 100644 --- a/scm-ui/ui-webapp/src/repos/branches/containers/BranchTableWrapper.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/containers/BranchTableWrapper.tsx @@ -30,19 +30,9 @@ import { useTranslation } from "react-i18next"; import { useBranchDetailsCollection } from "@scm-manager/ui-api"; import { KeyboardIterator } from "@scm-manager/ui-shortcuts"; import BranchList from "../components/BranchList"; -import { Collapsible } from "@scm-manager/ui-layout"; -import { LinkButton } from "@scm-manager/ui-buttons"; +import { Collapsible, DataPageHeader } from "@scm-manager/ui-layout"; import { Select } from "@scm-manager/ui-forms"; import { SORT_OPTIONS, SortOption } from "../../tags/orderTags"; -import styled from "styled-components"; - -const BranchListWrapper = styled.div` - gap: 1rem; -`; - -const HeaderWrapper = styled.div` - gap: 0.5rem 1rem; -`; type Props = { repository: Repository; @@ -74,26 +64,32 @@ const BranchTableWrapper: FC = ({ repository, baseUrl, data }) => { <> - -
- - -
+ + + + {({ formFieldId }) => ( + <> + + {t("branches.overview.sort.label")} + + + + + + )} + + {showCreateButton ? ( - - {t("branches.overview.createButton")} - + {t("branches.overview.createButton")} ) : null} -
- + +
{activeBranches.length > 0 ? ( @@ -116,7 +112,7 @@ const BranchTableWrapper: FC = ({ repository, baseUrl, data }) => { ) : null} - +
); };