great-expectations / jsonforms-antd-renderers

MIT License
10 stars 3 forks source link

categorization support #100

Open LuoShiXi opened 6 days ago

LuoShiXi commented 6 days ago

hi, master please, any time support the categorization ?

https://jsonforms.io/docs/uischema/layouts#categorization

thanks

Typiqally commented 5 days ago

I have some code for this, just haven't had the time to make a proper PR for it. If you want it I can send you a gist.

DrewHoo commented 5 days ago

@LuoShiXi Thanks for reaching out! Our original implementation included Categorization Layout elements, but we found that use case to be really thin (our original use case for Categorization elements was more easily solved outside of jsonforms) and it comes with a fairly complex implementation (ie validation at different steps of Categorization is an unsolved problem in the official renderer libraries AFAICT). That said, could you tell us more about your use case? Maybe share some proposed validation and ui schemas? @Typiqally we'd love to hear about your use case as well!

LuoShiXi commented 4 days ago

I have some code for this, just haven't had the time to make a proper PR for it. If you want it I can send you a gist.

Not for now, thanks

LuoShiXi commented 4 days ago

@LuoShiXi Thanks for reaching out! Our original implementation included Categorization Layout elements, but we found that use case to be really thin (our original use case for Categorization elements was more easily solved outside of jsonforms) and it comes with a fairly complex implementation (ie validation at different steps of Categorization is an unsolved problem in the official renderer libraries AFAICT). That said, could you tell us more about your use case? Maybe share some proposed validation and ui schemas? @Typiqally we'd love to hear about your use case as well!

@Typiqally @DrewHoo Thanks for your reply. My usage scenario is that I have a k8s application deployment platform, and then there is a set of configurations for each application (for example: nginx, zookeeper, etc.). Because there are many applications, I hope to implement Tabs switching by grouping instead of full-screen sliding display, which can provide a better user experience. The following is a similar screenshot (it has not yet implemented the form mode)

image

And now I also tried to use Group to achieve my purpose. I hope that Group can be folded, but I found that it is not possible because it is a Card type instead of a folding panel.

Typiqally commented 1 day ago

Hi, our use case is quite similar. We have quite a large configuration form for configuring one of our systems in the cloud. To improve UX, it is better to separate the form into groups using tabs.

I used the following code to add this, it is based on the official Material UI renderers from JSONForms. Feel free to use it. I'm also happy to make a PR, but that'll have to wait until I have some more free time.

import { useState, useMemo } from 'react';
import {
  and,
  Categorization,
  Category,
  deriveLabelForUISchemaElement,
  getAjv,
  isVisible,
  rankWith,
  StatePropsOfLayout,
  Tester,
  UISchemaElement,
  uiTypeIs
} from '@jsonforms/core';
import {
  TranslateProps,
  useJsonForms,
  withJsonFormsLayoutProps,
  withTranslateProps
} from '@jsonforms/react';
import { Tabs } from 'antd';
import { AntDLayout, AntDLayoutProps } from './AntdLayoutRenderer.tsx';

export const isSingleLevelCategorization: Tester = and(
  uiTypeIs('Categorization'),
  (uischema: UISchemaElement): boolean => {
    const categorization = uischema as Categorization;

    return (
      categorization.elements &&
      categorization.elements.reduce(
        (acc, e) => acc && e.type === 'Category',
        true
      )
    );
  }
);

export const antdCategorizationLayoutTester = rankWith(
  2,
  and(uiTypeIs('Categorization'), isSingleLevelCategorization)
);

export interface AntdCategorizationLayoutRendererProps
  extends StatePropsOfLayout,
    TranslateProps {
  tabDirection?: 'ltr' | 'rtl';
  tabPosition?: 'left' | 'right' | 'top' | 'bottom';
  selected?: number;

  onChange?(selected: number, prevSelected: number): void;
}

const AntdCategorizationLayout = (
  {
    data,
    path,
    renderers,
    cells,
    schema,
    uischema,
    visible,
    enabled,
    selected,
    tabDirection = 'ltr',
    tabPosition = 'top',
    t,
    onChange
  }: AntdCategorizationLayoutRendererProps) => {
  const ctx = useJsonForms();
  const ajv = getAjv({ jsonforms: { ...ctx } });

  const categorization = uischema as Categorization;
  const [previousCategorization, setPreviousCategorization] =
    useState<Categorization>(uischema as Categorization);
  const [activeCategoryIndex, setActiveCategoryIndex] = useState<number>(
    selected ?? 0
  );
  const categories = useMemo(() => {
    return categorization.elements.filter(
      (category: Category | Categorization) => {
        return isVisible(category, data, path, ajv);
      }
    );
  }, [ajv, categorization.elements, data, path]);

  if (categorization !== previousCategorization) {
    setActiveCategoryIndex(0);
    setPreviousCategorization(categorization);
  }

  const childProps: AntDLayoutProps = {
    elements: [],
    schema,
    path,
    enabled,
    visible,
    renderers,
    cells
  };

  const onTabChange = (key: string) => {
    const keyIndex = parseInt(key);

    if (onChange) {
      onChange(keyIndex, activeCategoryIndex);
    }

    setActiveCategoryIndex(keyIndex);
  };

  const tabItems = useMemo(
    () =>
      categories.map((e: Category | Categorization, index: number) => ({
        key: index.toString(),
        label: deriveLabelForUISchemaElement(e, t),
        children: (
          <AntDLayout
            {...childProps}
            key={index}
            elements={categories[index] ? categories[index].elements : []}
          />
        )
      })),
    [categories, childProps, t]
  );

  if (!visible) {
    return null;
  }

  return (
    <Tabs
      items={tabItems}
      onTabClick={onTabChange}
      activeKey={activeCategoryIndex.toString()}
      direction={tabDirection}
      tabPosition={tabPosition}
    />
  );
};

export default withTranslateProps(
  withJsonFormsLayoutProps(AntdCategorizationLayout)
);

Please note that I used my own version of AntDLayout. This is simply a copy of the original one, as it isn't exported by the package.

DrewHoo commented 2 hours ago

Thank you @LuoShiXi and @Typiqally for the use case descriptions! I'm taking this to our team to review, and will get back to you by end of week.