Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request : Dynamic Layout #37

Open
MichaelHeinzman opened this issue Jan 19, 2025 · 0 comments
Open

Feature Request : Dynamic Layout #37

MichaelHeinzman opened this issue Jan 19, 2025 · 0 comments

Comments

@MichaelHeinzman
Copy link

MichaelHeinzman commented Jan 19, 2025

Currently there's not really a good way to dynamically organize the layout for different PLP formats. Our company had to fork this repo and customize it ourselves which led to a lot of development work. I think there's a need to add an option in the config to organize different layouts for different screen sizes. This can definitely be done since I've done it on a personal project with this as a test run.

All of the data is saved in context anyways so I would recommend removing props being passed to the main components on the page and accessing them through the use of context providers instead.

These are the list of components that I would recommend to create and allow to be moved around on the page.

  Title,
  SearchBar,
  SortDropdown,
  ProductList,
  FilterButton,
  SelectedFilters,
  Facets,
  Pagination,
  etc...

The configuration that can be passed to the project to dynamically structure the layout of the PLP can be as follows (or if there's a better way do that). The point is that I can remove or add 'columns' in this case but really they are just containers that load the components I list in the array.

// layoutConfig.ts

interface LayoutVariant {
  columns: Array<{
    id: string;
    width?: string; // optional inline styling
    flex?: string; // optional inline styling
    components: string[];
  }>;
}

interface ResponsiveLayoutConfig {
  default: LayoutVariant;
  mobile?: LayoutVariant;
  tablet?: LayoutVariant;
  desktop?: LayoutVariant;
}

export const layoutConfig: ResponsiveLayoutConfig = {
  // The base fallback layout: "default"
  default: {
    columns: [
      {
        id: 'topLeft',
        components: ['Title', 'FilterButton'],
      },
      {
        id: 'topRight',
        components: ['SearchBar', 'SortDropdown'],
      },
      {
        id: 'leftColumn',
        components: ['SelectedFilters', 'Facets'],
      },
      {
        id: 'rightColumn',
        components: ['ProductList'],
      },
      {
        id: 'bottom',
        components: ['Pagination'],
      },
    ],
  },

  mobile: {
    columns: [
      {
        id: 'top',
        components: ['Title'],
      },
      {
        id: 'middle',
        components: [
          'FilterButton',
          'SortDropdown',
          'FiltersSelected',
          'Facets',
          'ProductList',
        ],
      },
      {
        id: 'bottom',
        components: ['Pagination'],
      },
    ],
  },
};

The component that would organize the PLP based on this would be DynamicLayout.tsx which reads this config and renders components in that order. You can't just organize things with CSS since moving things around with CSS is an accessibility concern.


import { FunctionComponent } from 'preact';
import { useSensor } from 'src/context';
import { componentMap } from 'src/utils/componentMap';
import { layoutConfig } from 'src/utils/layoutConfig';

const DynamicLayout: FunctionComponent = () => {
  const { screenSize } = useSensor();
  // screenSize = { mobile: bool, tablet: bool, desktop: bool, columns: number }

  // Pick which layout to use. Fallback to `default`.
  let activeLayout = layoutConfig.default;
  if (screenSize.mobile && layoutConfig.mobile) {
    activeLayout = layoutConfig.mobile;
  } else if (screenSize.tablet && layoutConfig.tablet) {
    activeLayout = layoutConfig.tablet;
  } else if (screenSize.desktop && layoutConfig.desktop) {
    activeLayout = layoutConfig.desktop;
  }

  return (
    <>
      {activeLayout.columns.map((column) => {
        const { id, components } = column;
        return (
          <div key={id} id={id}>
            {components.map((componentKey) => {
              const Component = componentMap[componentKey];
              if (!Component) {
                // Optionally throw an error or skip
                return null;
              }
              return <Component key={componentKey} />;
            })}
          </div>
        );
      })}
    </>
  );
};

export default DynamicLayout;

The project doesn't have to be restructured significantly since everything is using context anyways so it would be a matter of moving things from props and just pulling them straight from each context provider. You would also need to remove the wrappers around each component that don't really serve a purpose such as the CategoryFilters, ProductsContainer, ProductsHeader. The content inside these can be destructured into their own components so that it can be called within the layout object.

For example, CategoryFilters.tsx contains the title, results, filterbutton and facets. You can remove CategoryFilters component and just have the Title, Results, FilterButton, and Facets components. The data that Title, Results, FilterButton and Facets use all come from context providers so the context can just be called in each component to access the data. No need to pass props to them.

ProductsContainer is very similar, it has Pagination, ProductList, ProductCardShimmer, NoResults. These can be separated into their own components and called with the DynamicLayout.

My main reason for pointing this out is we can reduce the code and allow people to organize the PLP how they want. Currently it's very limited in terms of customizability. My project with the company I work for had to immediately resort to a fork of the project, it would be nice to eventually be able to just use the boilerplate for the PLP but it's currently not in a state where that's possible. I would recommend looking at how the dropins for EDS was done. They are highly extendable and customizable and I think an approach like that would benefit this project. Any slight variation in the design of the PLP and someone would need to fork this project and redo the code themselves. A way to override a certain component would be nice too so that we can at least override features instead of resorting to forking if we need something specific.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant