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

feat(layout): introduce Layout API #499

Open
wants to merge 26 commits into
base: next
Choose a base branch
from
Open

Conversation

francoischalifour
Copy link
Member

@francoischalifour francoischalifour commented Mar 25, 2021

This introduces the Layout API via a new package: @algolia/autocomplete-layout-classic. The styles are included in the Autocomplete Classic Theme, which is required for these layouts.

Goal

Layouts aim at providing minimal UI elements that are common in Autocomplete experiences.

What's included

  • Navigation command keys. Accessibility hints to use the autocomplete from the keyboard.
  • Search by Algolia logo. The official Algolia footer for free plans.

Preview

image

Usage

Basic

autocomplete({
  // ...
  render({ sections, createElement, Fragment }, root) {
    render(
      <Fragment>
        <div className="aa-PanelLayout">{sections}</div>
        <footer className="aa-PanelFooter">
          {NavigationCommandsLayout({
            createElement,
            Fragment,
          })}
          {SearchByAlgoliaLayout({
            createElement,
            Fragment,
          })}
        </footer>
      </Fragment>,
      root
    );
  },
});

With translations

autocomplete({
  // ...
  render({ sections, createElement, Fragment }, root) {
    render(
      <Fragment>
        <div className="aa-PanelLayout">{sections}</div>
        <footer className="aa-PanelFooter">
          {NavigationCommandsLayout({
            createElement,
            Fragment,
            translations: {
              toClose: 'pour fermer',
              toNavigate: 'pour naviguer',
              toSelect: 'pour sélectionner',
            },
          })}
          {SearchByAlgoliaLayout({
            createElement,
            Fragment,
            translations: {
              searchBy: 'Recherche par',
            },
          })}
        </footer>
      </Fragment>,
      root
    );
  },
});

Documentation

You can read the documentation written in this PR.

What's next

There are still a few styling issues that need to be handled:

  • Padding with and without footer
  • Detached mode styles

Feedback

I'm open to feedback about this API. If you have any suggestions on how to make this usage easier, let me know!

@codesandbox-ci
Copy link

codesandbox-ci bot commented Mar 25, 2021

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit dad554b:

Sandbox Source
@algolia/autocomplete-example-github-repositories-custom-plugin Configuration
@algolia/autocomplete-example-layouts Configuration
@algolia/autocomplete-example-playground Configuration
@algolia/autocomplete-example-query-suggestion-with-categories Configuration
@algolia/autocomplete-example-query-suggestions-with-hits Configuration
@algolia/autocomplete-example-query-suggestions-with-inline-categories Configuration
@algolia/autocomplete-example-query-suggestions-with-recent-searches Configuration
@algolia/autocomplete-example-query-suggestions Configuration
@algolia/autocomplete-example-react-renderer Configuration
@algolia/autocomplete-example-recently-viewed-items Configuration

Copy link
Member

@sarahdayan sarahdayan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have other use cases in mind for the Layout API? For example, would we imagine packaging the templates from the classic theme, or providing an out-of-the-box preview panel layout?

I'm asking because the current API only exports two components and I'm trying to reason about what's the higher-level usage we imagine for it.


> `(type: any, props: Record<string, any> | null, ...children: ComponentChildren[]) => VNode`

The function that create virtual nodes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The function that create virtual nodes.
The function to create virtual nodes.

Copy link
Contributor

@Shipow Shipow Mar 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have other use cases in mind for the Layout API? For example, would we imagine packaging the templates from the classic theme, or providing an out-of-the-box preview panel layout?

I'm asking because the current API only exports two components and I'm trying to reason about what's the higher-level usage we imagine for it.

Ideally we provide everything that could impact the navigation or accessibility. Although we don't provide anything to style inside the preview (not even the simplest display) from there it's clear that there is nothing more to learn or take advantage in autocomplete. Eventually we could provide some utility class for very common patterns.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See e4e43ca.

packages/website/docs/autocomplete-layout-classic.md Outdated Show resolved Hide resolved

.aa-NavigationCommandLabel,
.aa-SearchByAlgoliaLabel {
color: #969faf;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not customizable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to support dark mode as well.

@Haroenv
Copy link
Contributor

Haroenv commented Mar 25, 2021

I would prefer an api where the layouts would infer the createElement based on the renderer that would also be used for autocomplete, something like:

import {
  createNavigationCommandsLayout,
  createSearchByAlgoliaLayout,
} from 'layouts';
const NavigationCommandsLayout = createNavigationCommandsLayout({
  createElement,
  Fragment,
});
const SearchByAlgoliaLayout = createSearchByAlgoliaLayout({
  createElement,
  Fragment,
});
autocomplete({
  // ...
  renderer: { createElement, Fragment },
  render({ sections }, root) {
    render(
      <Fragment>
        <div className="aa-PanelLayout">{sections}</div>
        <footer className="aa-PanelFooter">
          <NavigationCommandsLayout
            translations={{
              toClose: 'pour fermer',
              toNavigate: 'pour naviguer',
              toSelect: 'pour sélectionner',
            }}
          />
          <SearchByAlgoliaLayout />
        </footer>
      </Fragment>,
      root
    );
  },
});

The main criticism I have here & as well for the highlight functions is that passing createElement & Fragment is noisy. If we register the helpers / components somehow to the autocomplete instance, I think those could be inferred.

Alternative:

import { NavigationCommandsLayout, SearchByAlgoliaLayout } from 'layouts';

autocomplete({
  // ...
  renderer: { createElement, Fragment },
  components: { SearchByAlgoliaLayout, NavigationCommandsLayout },
  render({ sections, components }, root) {
    render(
      <Fragment>
        <div className="aa-PanelLayout">{sections}</div>
        <footer className="aa-PanelFooter">
          <components.NavigationCommandsLayout
            translations={{
              toClose: 'pour fermer',
              toNavigate: 'pour naviguer',
              toSelect: 'pour sélectionner',
            }}
          />
          <components.SearchByAlgoliaLayout />
        </footer>
      </Fragment>,
      root
    );
  },
});

(in the second option you could instantiate the component using the createElement & Fragment passed to autocomplete as well without bundling the layouts with autocomplete)

@francoischalifour
Copy link
Member Author

Do we have other use cases in mind for the Layout API? For example, would we imagine packaging the templates from the classic theme, or providing an out-of-the-box preview panel layout?

I'm asking because the current API only exports two components and I'm trying to reason about what's the higher-level usage we imagine for it.

@sarahdayan We originally wanted to export components like ItemLayout, etc. but we think that it's too early to take this decision. Now that we document the markup for it, it wouldn't provide much value, but rather slow us down once we want to make more advanced templates.

Once we're settled on great default templates for items, preview panels, etc., it can become part of this package.

Base automatically changed from feat/theme-patch to next April 1, 2021 10:09
@francoischalifour
Copy link
Member Author

I updated the API to use our new Component API:

/** @jsx h */
import { autocomplete } from '@algolia/autocomplete-js';
import {
  NavigationCommands,
  SearchByAlgolia,
} from '@algolia/autocomplete-layout-classic';
import { h, render } from 'preact';

import '@algolia/autocomplete-theme-classic';

autocomplete({
  // ...
  components: {
    NavigationCommands,
    SearchByAlgolia,
  },
  render({ sections, Fragment, components }, root) {
    render(
      <Fragment>
        <div className="aa-PanelLayout aa-Panel--scrollable">{sections}</div>
        <footer className="aa-PanelFooter">
          <components.NavigationCommands />
          <components.SearchByAlgolia />
        </footer>
      </Fragment>,
      root
    );
  },
});

<Fragment>
<div className="aa-PanelLayout aa-Panel--scrollable">{sections}</div>
<footer className="aa-PanelFooter">
<components.NavigationCommands
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you in this case not use it directly, without passing to components?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my linter doesn't detect that components variable is used in the code. I guess because it struggles to detect the html tag and make the relation? but it's not great.

// ...
renderer,
components: {
NavigationCommands: createNavigationCommandsComponent(renderer),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if we call the components with renderer ourselves before setting it to components?

Copy link
Contributor

@Haroenv Haroenv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks very sleek!

@francoischalifour
Copy link
Member Author

francoischalifour commented Jun 11, 2021

Putting "On Hold" because we de-prioritized the Layout API.

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

Successfully merging this pull request may close these issues.

None yet

4 participants