diff --git a/web/src/api/buster_rest/metrics/index.ts b/web/src/api/buster_rest/metrics/index.ts index c534ed4d2..cf25f89f5 100644 --- a/web/src/api/buster_rest/metrics/index.ts +++ b/web/src/api/buster_rest/metrics/index.ts @@ -1,3 +1,2 @@ -export * from './interfaces'; export * from './requests'; export * from './queryRequests'; diff --git a/web/src/api/buster_rest/metrics/interfaces.ts b/web/src/api/buster_rest/metrics/interfaces.ts deleted file mode 100644 index b421be7c9..000000000 --- a/web/src/api/buster_rest/metrics/interfaces.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { BusterChartConfigProps } from '@/api/asset_interfaces/metric'; -import type { VerificationStatus } from '@/api/asset_interfaces/share'; - -export interface GetMetricParams { - id: string; - password?: string; - version_number?: number; //api will default to latest if not provided -} - -/** - * Request payload for updating metric properties - */ -export type UpdateMetricParams = { - /** The unique identifier of the metric to update */ - id: string; - /** New title for the metric */ - name?: string; - /** SQL query associated with the metric */ - sql?: string; - chart_config?: BusterChartConfigProps; - /** Flag to save the current draft state */ - save_draft?: boolean; - /** Admin only: verification status update */ - status?: VerificationStatus; - /** file in yaml format to update */ - file?: string; -}; diff --git a/web/src/api/buster_rest/metrics/queryRequests.ts b/web/src/api/buster_rest/metrics/queryRequests.ts index db148b637..913d1b7c9 100644 --- a/web/src/api/buster_rest/metrics/queryRequests.ts +++ b/web/src/api/buster_rest/metrics/queryRequests.ts @@ -14,7 +14,6 @@ import { unshareMetric, updateMetricShare } from './requests'; -import type { GetMetricParams } from './interfaces'; import { prepareMetricUpdateMetric, upgradeMetricToIMetric } from '@/lib/metrics'; import { metricsQueryKeys } from '@/api/query_keys/metric'; import { collectionQueryKeys } from '@/api/query_keys/collection'; @@ -60,7 +59,10 @@ export const useGetMetric = ( }); }; -export const prefetchGetMetric = async (params: GetMetricParams, queryClientProp?: QueryClient) => { +export const prefetchGetMetric = async ( + params: Parameters[0], + queryClientProp?: QueryClient +) => { const queryClient = queryClientProp || new QueryClient(); await queryClient.prefetchQuery({ ...metricsQueryKeys.metricsGetMetric(params.id), diff --git a/web/src/api/buster_rest/metrics/requests.ts b/web/src/api/buster_rest/metrics/requests.ts index 523c3e06c..7683179b9 100644 --- a/web/src/api/buster_rest/metrics/requests.ts +++ b/web/src/api/buster_rest/metrics/requests.ts @@ -1,7 +1,7 @@ import { mainApi } from '../instances'; import { serverFetch } from '@/api/createServerInstance'; -import type { GetMetricParams, UpdateMetricParams } from './interfaces'; import type { + BusterChartConfigProps, BusterMetric, BusterMetricData, BusterMetricListItem @@ -13,7 +13,15 @@ import type { } from '@/api/asset_interfaces/shared_interfaces'; import { VerificationStatus } from '@/api/asset_interfaces/share'; -export const getMetric = async ({ id, password, version_number }: GetMetricParams) => { +export const getMetric = async ({ + id, + password, + version_number +}: { + id: string; + password?: string; + version_number?: number; //api will default to latest if not provided +}) => { return mainApi .get(`/metrics/${id}`, { params: { password, version_number } @@ -21,7 +29,7 @@ export const getMetric = async ({ id, password, version_number }: GetMetricParam .then((res) => res.data); }; -export const getMetric_server = async ({ id, password }: GetMetricParams) => { +export const getMetric_server = async ({ id, password }: Parameters[0]) => { return await serverFetch(`/metrics/${id}`, { params: { ...(password && { password }) } }); @@ -54,7 +62,21 @@ export const listMetrics_server = async (params: Parameters[ return await serverFetch('/metrics', { params }); }; -export const updateMetric = async (params: UpdateMetricParams) => { +export const updateMetric = async (params: { + /** The unique identifier of the metric to update */ + id: string; + /** New title for the metric */ + name?: string; + /** SQL query associated with the metric */ + sql?: string; + chart_config?: BusterChartConfigProps; + /** Flag to save the current draft state */ + save_draft?: boolean; + /** Admin only: verification status update */ + status?: VerificationStatus; + /** file in yaml format to update */ + file?: string; +}) => { return mainApi.put(`/metrics/${params.id}`, params).then((res) => res.data); }; diff --git a/web/src/components/features/ShareMenu/AccessDropdown.tsx b/web/src/components/features/ShareMenu/AccessDropdown.tsx index ecb1fcaf1..a6d54e4eb 100644 --- a/web/src/components/features/ShareMenu/AccessDropdown.tsx +++ b/web/src/components/features/ShareMenu/AccessDropdown.tsx @@ -11,29 +11,17 @@ import { cn } from '@/lib/classMerge'; type DropdownValue = ShareRole | 'remove' | 'notShared'; export const AccessDropdown: React.FC<{ - groupShare?: boolean; className?: string; showRemove?: boolean; shareLevel?: ShareRole | null; onChangeShareLevel?: (level: ShareRole | null) => void; -}> = ({ - shareLevel, - showRemove = true, - groupShare = false, - className = '', - onChangeShareLevel -}) => { +}> = ({ shareLevel, showRemove = true, className = '', onChangeShareLevel }) => { const disabled = useMemo(() => canEdit(shareLevel), [shareLevel]); const items = useMemo(() => { - const baseItems: DropdownItem[] = standardItems; + const baseItems: DropdownItem[] = [...standardItems]; - if (groupShare) { - baseItems.push({ - label: , - value: 'notShared' - }); - } else if (showRemove) { + if (showRemove) { baseItems.push({ label: 'Remove', value: 'remove' @@ -44,7 +32,7 @@ export const AccessDropdown: React.FC<{ ...item, selected: item.value === shareLevel })); - }, [groupShare, showRemove, shareLevel]); + }, [showRemove, shareLevel]); const selectedLabel = useMemo(() => { const selectedItem = items.find((item) => item.selected) || OWNER_ITEM; diff --git a/web/src/components/features/ShareMenu/ShareMenuContentBody.tsx b/web/src/components/features/ShareMenu/ShareMenuContentBody.tsx index a5137e837..c5ecd1716 100644 --- a/web/src/components/features/ShareMenu/ShareMenuContentBody.tsx +++ b/web/src/components/features/ShareMenu/ShareMenuContentBody.tsx @@ -174,7 +174,6 @@ const ShareMenuContentShare: React.FC = React.memo( {inputValue && ( )} - + {header && scrollable && headerBorderVariant === 'ghost' && (
)} diff --git a/web/src/components/ui/layouts/AppPageLayoutHeader.tsx b/web/src/components/ui/layouts/AppPageLayoutHeader.tsx index 7b88d7a6e..a1de95cd3 100644 --- a/web/src/components/ui/layouts/AppPageLayoutHeader.tsx +++ b/web/src/components/ui/layouts/AppPageLayoutHeader.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; const headerVariants = cva( - 'bg-page-background flex max-h-[38px] min-h-[38px] items-center justify-between gap-x-2.5 relative', + 'bg-page-background flex max-h-[38px] min-h-[38px] items-center z-10 justify-between gap-x-2.5 relative', { variants: { sizeVariant: { diff --git a/web/src/components/ui/scroll-area/ScrollArea.stories.tsx b/web/src/components/ui/scroll-area/ScrollArea.stories.tsx new file mode 100644 index 000000000..f91a7fa28 --- /dev/null +++ b/web/src/components/ui/scroll-area/ScrollArea.stories.tsx @@ -0,0 +1,119 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { ScrollArea } from './ScrollArea'; + +const meta = { + title: 'UI/ScrollArea/ScrollArea', + component: ScrollArea, + parameters: { + layout: 'centered' + }, + tags: ['autodocs'] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam auctor, nisl eget + ultricies tincidunt, nisl nisl aliquam nisl, eget ultricies nisl nisl eget nisl. +

+

+ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque + laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi + architecto beatae vitae dicta sunt explicabo. +

+

+ Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia + consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. +

+

+ Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci + velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam + quaerat voluptatem. +

+

+ Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, + nisi ut aliquid ex ea commodi consequatur. +

+
+
+ ) +}; + +export const TallContent: Story = { + render: () => ( + +
+ {Array.from({ length: 50 }).map((_, i) => ( +
+ Row {i + 1} +
+ ))} +
+
+ ) +}; + +export const WideContent: Story = { + render: () => ( + +
+

+ This content is intentionally wider than the container to demonstrate horizontal + scrolling. The ScrollArea component will automatically add scrollbars as needed. +

+
+ {Array.from({ length: 10 }).map((_, i) => ( +
+ {i + 1} +
+ ))} +
+
+
+ ) +}; + +export const CustomHeight: Story = { + render: () => ( + +
+

Taller Scroll Area

+ {Array.from({ length: 20 }).map((_, i) => ( +

+ Paragraph {i + 1}: Lorem ipsum dolor sit amet, consectetur adipiscing elit. +

+ ))} +
+
+ ) +}; + +export const NestedContent: Story = { + render: () => ( + +
+

Nested Content Example

+
+

Main content area with some text.

+
+

Nested Section

+

This is nested content inside the scroll area.

+
+
+ {Array.from({ length: 10 }).map((_, i) => ( +
+

Section {i + 1}

+

Additional content for demonstrating scrolling behavior.

+
+ ))} +
+
+ ) +}; diff --git a/web/src/lib/metrics/saveToServerHelpers.ts b/web/src/lib/metrics/saveToServerHelpers.ts index 1042e79f7..2fa50686d 100644 --- a/web/src/lib/metrics/saveToServerHelpers.ts +++ b/web/src/lib/metrics/saveToServerHelpers.ts @@ -16,7 +16,7 @@ import type { PieChartAxis, ScatterAxis } from '@/api/asset_interfaces/metric/charts'; -import { UpdateMetricParams } from '@/api/buster_rest/metrics'; +import { type updateMetric } from '@/api/buster_rest/metrics'; const DEFAULT_COLUMN_SETTINGS_ENTRIES = Object.entries(DEFAULT_COLUMN_SETTINGS); const DEFAULT_COLUMN_LABEL_FORMATS_ENTRIES = Object.entries(DEFAULT_COLUMN_LABEL_FORMAT); @@ -120,11 +120,11 @@ const getChangesFromDefaultChartConfig = (newMetric: IBusterMetric) => { export const prepareMetricUpdateMetric = ( newMetric: IBusterMetric, oldMetric: IBusterMetric -): UpdateMetricParams | null => { +): Parameters[0] | null => { const changedTopLevelValues = getChangedTopLevelMessageValues( newMetric, oldMetric - ) as unknown as UpdateMetricParams; + ) as unknown as Parameters[0]; const changedChartConfig = getChangesFromDefaultChartConfig(newMetric); diff --git a/web/src/styles/buster.scss b/web/src/styles/buster.scss index 09481de39..5f71a4aad 100644 --- a/web/src/styles/buster.scss +++ b/web/src/styles/buster.scss @@ -7,22 +7,23 @@ } .scroll-shadow-container { - @apply relative; + @apply relative overflow-y-auto; + scroll-timeline-name: --scrollShadowContainer; .scroll-header { @apply fixed top-[30px] right-0 left-0 h-2 w-full; animation: shadowAnimation linear; - animation-range: 0px 125px; - animation-timeline: scroll(block start); + animation-range: 0px 100px; + animation-timeline: --scrollShadowContainer; animation-fill-mode: forwards; } @keyframes shadowAnimation { from { - box-shadow: 0 0 0 rgba(0, 0, 0, 0); + box-shadow: 0px 0px 0px 0px var(--color-border) !important; } to { - box-shadow: 0px 1px 8px 0px #00000029; + box-shadow: 0px 0px 0px 0.5px var(--color-border) !important; } } }