Add bulk request access to portal documents
Visitors could only request access to one document, report, or file at a time. Add row checkboxes and a bottom selection toolbar to the compliance portal documents page so a visitor can select several rows and request access to all still-locked ones in a single round-trip. Expose a selection-scoped requestAccesses mutation that forwards the chosen id lists to the existing RequestPortalAccess service (one transaction, one NDA/auth gate). The resolver loads and tenant-checks every target before requesting so a foreign id is rejected before any access row is written, and echoes the affected nodes so the client flips each row to pending in place. Add a styled Base UI Checkbox to the v2 kit, a local selection context shared by the independent row fragments, and mirror the new selection strings across all locales. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
56
packages/ui/src/v2/Checkbox/Checkbox.stories.tsx
Normal file
56
packages/ui/src/v2/Checkbox/Checkbox.stories.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// 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 type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
import { Checkbox } from "./Checkbox";
|
||||
import { CheckboxSkeleton } from "./CheckboxSkeleton";
|
||||
|
||||
export default {
|
||||
title: "v2/Checkbox",
|
||||
component: Checkbox,
|
||||
args: {
|
||||
"aria-label": "Checkbox",
|
||||
},
|
||||
} satisfies Meta<typeof Checkbox>;
|
||||
|
||||
type Story = StoryObj<typeof Checkbox>;
|
||||
|
||||
export const Playground: Story = {};
|
||||
|
||||
export const States: Story = {
|
||||
render: () => (
|
||||
<div className="flex items-center gap-4">
|
||||
<Checkbox aria-label="Unchecked" />
|
||||
<Checkbox aria-label="Checked" defaultChecked />
|
||||
<Checkbox aria-label="Indeterminate" indeterminate />
|
||||
<Checkbox aria-label="Disabled" disabled />
|
||||
<Checkbox aria-label="Disabled checked" disabled defaultChecked />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const Skeleton: Story = {
|
||||
render: () => (
|
||||
<div className="flex items-center gap-4">
|
||||
<CheckboxSkeleton />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
45
packages/ui/src/v2/Checkbox/Checkbox.tsx
Normal file
45
packages/ui/src/v2/Checkbox/Checkbox.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// 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 { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
|
||||
import { CheckIcon, MinusIcon } from "@phosphor-icons/react";
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
import { checkbox, checkboxIndicator } from "./variants";
|
||||
|
||||
export type CheckboxProps
|
||||
= & Omit<ComponentProps<typeof BaseCheckbox.Root>, "className">
|
||||
& {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
// Styled Base UI checkbox. Controlled with `checked` / `onCheckedChange` (or
|
||||
// uncontrolled via `defaultChecked`); `indeterminate` renders a mixed state.
|
||||
export function Checkbox(props: CheckboxProps) {
|
||||
const { className, indeterminate, ...rest } = props;
|
||||
|
||||
return (
|
||||
<BaseCheckbox.Root indeterminate={indeterminate} className={checkbox({ className })} {...rest}>
|
||||
<BaseCheckbox.Indicator className={checkboxIndicator()}>
|
||||
{indeterminate ? <MinusIcon weight="bold" /> : <CheckIcon weight="bold" />}
|
||||
</BaseCheckbox.Indicator>
|
||||
</BaseCheckbox.Root>
|
||||
);
|
||||
}
|
||||
32
packages/ui/src/v2/Checkbox/CheckboxSkeleton.tsx
Normal file
32
packages/ui/src/v2/Checkbox/CheckboxSkeleton.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// 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 type { ComponentProps } from "react";
|
||||
|
||||
import { checkboxSkeleton } from "./variants";
|
||||
|
||||
export type CheckboxSkeletonProps = Omit<ComponentProps<"span">, "children">;
|
||||
|
||||
// Loading placeholder paired with Checkbox: a pulse block matching its box.
|
||||
export function CheckboxSkeleton(props: CheckboxSkeletonProps) {
|
||||
const { className, ...rest } = props;
|
||||
|
||||
return <span className={checkboxSkeleton({ className })} {...rest} aria-hidden />;
|
||||
}
|
||||
41
packages/ui/src/v2/Checkbox/variants.ts
Normal file
41
packages/ui/src/v2/Checkbox/variants.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// 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 { tv } from "tailwind-variants/lite";
|
||||
|
||||
// A 20px control box (Radix "Checkbox"). The checked/indeterminate surface and
|
||||
// the disabled treatment resolve off Base UI's data-* state attributes.
|
||||
export const checkbox = tv({
|
||||
base: [
|
||||
"inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-2 border border-sand-a7 bg-sand-1 text-gold-1 outline-none transition-colors",
|
||||
"focus-visible:ring-2 focus-visible:ring-gold-8",
|
||||
"data-[checked]:border-gold-12 data-[checked]:bg-gold-12",
|
||||
"data-[indeterminate]:border-gold-12 data-[indeterminate]:bg-gold-12",
|
||||
"data-[disabled]:cursor-not-allowed data-[disabled]:border-sand-a3 data-[disabled]:bg-sand-2",
|
||||
],
|
||||
});
|
||||
|
||||
export const checkboxIndicator = tv({
|
||||
base: "flex items-center justify-center text-current [&_svg]:size-3.5",
|
||||
});
|
||||
|
||||
export const checkboxSkeleton = tv({
|
||||
base: "inline-block size-5 shrink-0 animate-pulse rounded-2 bg-sand-3 align-middle",
|
||||
});
|
||||
Reference in New Issue
Block a user