Skip to content

Conversation

@AndrewMusgrave
Copy link
Member

@AndrewMusgrave AndrewMusgrave commented Sep 24, 2019

WHY are these changes introduced?

Part of #1995

WHAT is this pull request doing?

Convert DateSelector to a functional component

Notes

I noticed while converting this component,the setState callback was used quite often however I couldn't figure out why, except when setting state with the date. I'm adding @dleroux since he'll have the most context on this

How to 🎩

Playground with date filter

Copy-paste this code in playground/Playground.tsx:
import React from 'react';
import {ResourceList, TextStyle, Avatar, FilterType, Card} from '../src';

export class Playground extends React.Component {
  state = {
    searchValue: '',
    appliedFilters: [
      {
        key: 'accountStatusFilter',
        value: 'Account enabled',
      },
    ],
  };

  handleSearchChange = (searchValue) => {
    this.setState({searchValue});
  };

  handleFiltersChange = (appliedFilters) => {
    this.setState({appliedFilters});
  };

  renderItem = (item) => {
    const {id, url, name, location} = item;
    const media = <Avatar customer size="medium" name={name} />;

    return (
      <ResourceList.Item id={id} url={url} media={media}>
        <h3>
          <TextStyle variation="strong">{name}</TextStyle>
        </h3>
        <div>{location}</div>
      </ResourceList.Item>
    );
  };

  render() {
    const resourceName = {
      singular: 'customer',
      plural: 'customers',
    };

    const items = [
      {
        id: 341,
        url: 'customers/341',
        name: 'Mae Jemison',
        location: 'Decatur, USA',
      },
      {
        id: 256,
        url: 'customers/256',
        name: 'Ellen Ochoa',
        location: 'Los Angeles, USA',
      },
    ];

    const filters = [
      {
        key: 'accountStatusFilter',
        label: 'Date selector',
        type: FilterType.DateSelector,
        minKey: '0',
        maxKey: '30',
      },
    ];

    const filterControl = (
      <ResourceList.FilterControl
        filters={filters}
        appliedFilters={this.state.appliedFilters}
        onFiltersChange={this.handleFiltersChange}
        searchValue={this.state.searchValue}
        onSearchChange={this.handleSearchChange}
        additionalAction={{
          content: 'Save',
          onAction: () => console.log('New filter saved'),
        }}
      />
    );

    return (
      <Card>
        <ResourceList
          resourceName={resourceName}
          items={items}
          renderItem={this.renderItem}
          filterControl={filterControl}
        />
      </Card>
    );
  }
}

🎩 checklist

@AndrewMusgrave AndrewMusgrave changed the title [WIP] Hookify DateSelector [DateSelector] Hookify component Sep 24, 2019
@AndrewMusgrave AndrewMusgrave marked this pull request as ready for review September 24, 2019 21:27
@BPScott BPScott mentioned this pull request Sep 25, 2019
Copy link
Member

@BPScott BPScott left a comment

Choose a reason for hiding this comment

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

A few small inline comments.

I've not ran this and am unfamilar with the DateSelector component so I'd appreciated a pair of eyes from someone who knows this component

datePickerYear: this.now.getFullYear(),
initialConsumerFilterKey: this.props.filterKey,
};
// eslint-disable-next-line react/display-name
Copy link
Member

Choose a reason for hiding this comment

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

Can we reference the upstream bug, like we do in ContextualSaveBar

// This does have a display name, but the linting has a bug in it
// https://github.com/yannickcr/eslint-plugin-react/issues/2324
// eslint-disable-next-line react/display-name

}: DateSelectorProps) {
const now = new Date();

const {translate} = useI18n();
Copy link
Member

Choose a reason for hiding this comment

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

Existing code tends to do const i18n = useI18n(); (my personal preference) or const intl = useI18n(); (a hangover from before we renamed this) rather than destructure.

We should stick to one style throughout the codebase. Which one do you prefer?

Copy link
Member Author

Choose a reason for hiding this comment

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

I would definitely prefer i18n or intl, the name clashing seems odd, best to just stick with one. However, we usually deconstruct whenever possible in our code. So I'm wondering if we should use const {translate} = useI18n() to be consistent? I'm wondering if the reason we didn't originally deconstruct was that iirc web was used as a guideline and they can't deconstruct because they need access to the context, however, we don't but we just didn't deconstruct? 🤷‍♂ Either way as long as well consistent I'm ok with it.

Copy link
Member

Choose a reason for hiding this comment

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

i18n is an instance of the I18n class, so I'm kinda surprised that we can even destructure it in the first place. I thought destructuring only worked on objects.

To reduce others being suprised by that I'd be tempted to avoid destructuring and stick with const i18n = useI18n().

Copy link
Member Author

Choose a reason for hiding this comment

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

i18n is an instance of the I18n class, so I'm kinda surprised that we can even destructure it in the first place. I thought destructuring only worked on objects.

In JavaScript, mostly everything is going to be an object, even regex since it's not primitive. Classes themselves are not objects, but functions (typeof class Test {} === 'function') however they create a instance of the function in an object (typeof new Test === 'object'). The problem with destructuring is that it'll remove context.

class Test {
	constructor() {
  	this.num = 5;
  }

	func() {
  	return this.num;
  }

}

// All good
const t = new Test();
t.func();

// Losing context
const {func} = t;
func(); // Bad error ☹️

We don't use this within I18n so we're ok deconstructing.

To reduce others being suprised by that I'd be tempted to avoid destructuring and stick with const i18n = useI18n().

Two more points to support both sides of the argument.

Unless your playing with i18n you won't know it originated from a class.

If we don't deconstruct we'll be able to add features that do use this within having to refactor.

Copy link
Member

@BPScott BPScott Sep 30, 2019

Choose a reason for hiding this comment

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

Huh neat! It seems like I18n's translate function already uses this so by that logic this shouldn't even work now. And yet it seems to manage. That still feels like it's more by luck than judgement though, so I'd continue to prefer not destructuring

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops looks like we are. It's working because translate is class property (not currently part of JavaScript but in the works)

class Test {
  func = () => {}
}

Could compile down to something like

class Test {
  constructor() {
    this.func = () => {}
  }
}

Lamda functions lack certain contexts, including this and usually have the environment they were created in.

class Test {
  func = () => { console.log(this) }
}

const {func} = new Test;
func(); // All ok
class Test {
  func() { console.log(this) }
}

// Sorta gross alternative without using class properties still deconstructing
const test = new Test;
const {func} = test;
const boundFunc = func.bind(test);
boundFunc(); // All ok

Either way, I'm ok with not deconstructing our classes, it'll help future proof us and reduce the change of errors. We should reach out for other opinions as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

Talked it over with @dleroux and we're all on board with not deconstructing

@github-actions
Copy link
Contributor

github-actions bot commented Oct 3, 2019

Results

💦 Potential splash zone of changes introduced to src/**/*.tsx in this pull request:

Files modified4
Files potentially affected4

Details

All files potentially affected (total: 4)
UNRELEASED.md (total: 0)

Files potentially affected (total: 0)

🧩 src/components/ResourceList/components/FilterControl/components/DateSelector/DateSelector.tsx (total: 4)

Files potentially affected (total: 4)

🧩 src/components/ResourceList/components/FilterControl/components/DateSelector/index.ts (total: 4)

Files potentially affected (total: 4)

🧩 src/components/ResourceList/components/FilterControl/components/DateSelector/tests/DateSelector.test.tsx (total: 0)

Files potentially affected (total: 0)


This comment automatically updates as changes are made to this pull request.
Feedback, troubleshooting: open an issue or reach out on Slack in #polaris-tooling.

Copy link
Member

@BPScott BPScott left a comment

Choose a reason for hiding this comment

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

Nice one! I've not tested too thoroughly as I'm not that familiar with DateSelector but LGTM.

@AndrewMusgrave AndrewMusgrave merged commit 6e29a84 into master Oct 10, 2019
@AndrewMusgrave AndrewMusgrave deleted the hookify-date-selector branch October 10, 2019 15:17
@tmlayton tmlayton temporarily deployed to production October 16, 2019 00:32 Inactive
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

Successfully merging this pull request may close these issues.

3 participants