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

Migrate to TypeScript #300

Closed
Jetsly opened this issue Aug 23, 2018 · 44 comments
Closed

Migrate to TypeScript #300

Jetsly opened this issue Aug 23, 2018 · 44 comments

Comments

@Jetsly
Copy link

Jetsly commented Aug 23, 2018

No description provided.

@jeroenransijn
Copy link
Contributor

jeroenransijn commented Aug 24, 2018

Not yet. Looking into Flow/TypeScript for the future. Probably a couple of major version out though.

@pspeter3
Copy link

Would you take DTS PRs?

@jeroenransijn
Copy link
Contributor

@pspeter3 do you mean adding types? Not sure what DTS means.

@pspeter3
Copy link

A single DTS file can provide type definitions for the entire project. It means you can continue to write in JavaScript while supporting TypeScript users.

@paustint
Copy link

I would ❤️ to see typescript support!

The options are

  1. include a DTS file along with the project, as @pspeter3 mentioned - a type definition file that is hosted as part of the repository and would need to be included alongside the JS files in the build on npm.
    • This should be kept up-to-date and should be updated along with each new feature/change.
  2. Avoid including any type definitions within this project/repo and wait for the community to take ownership of this and publish the types here: https://github.com/DefinitelyTyped/DefinitelyTyped (which would then be published on npm as @types/evergreen)
    • The community would slowly keep this up-to-date as folks decide to contribute when changes occur.
  3. Convert your library to typescript to get full compile-time type safety - then during compile and build, .d.ts definition files are generated automatically to be included along with the JS files generated during build.

Option 3 would be the best :) (yes, I am biased because developing in typescript provides a much better developer experience compared to standard JS!)
Option 2 is the most common for non-ts projects, but option 1 is preferable if the evergreen team agrees to the additional amount of ongoing work to ensure the types remain up-to-date.

@zheeeng
Copy link

zheeeng commented Oct 23, 2018

Plz consider reopen this issue. Evergreen's UI looks beautiful very much, the only reason blocks me using it is the missing of type definitions.

@Idered
Copy link

Idered commented Oct 25, 2018

This is heavy based on segmentio/ui-box so I would start with adding typescript there

@balupton
Copy link

For the risk of being another +1, I just wanted to add in my support for this.

I deal with react a few months each year, and seems the best UI frameworks right now for React are:

  • Segment's Evergreen
  • Palantir's Blueprint (typescript)
  • Ant (typescript)
  • Shopify's Polaris (typescript)

After comparing their modal dialog solutions, seems order of easiest to hardest are:

  1. https://evergreen.segment.com/components/dialog
  2. https://ant.design/components/modal/
  3. https://polaris.shopify.com/components/overlays/modal#navigation
  4. https://blueprintjs.com/docs/#core/components/dialog

So it would be nice to see Evergreen be written in TypeScript, or for it to be published with TypeScript typings.

@balupton
Copy link

UI Box seems similar to https://github.com/jsxstyle/jsxstyle which is typescript, so perhaps that can be a start

@mshwery
Copy link
Contributor

mshwery commented Dec 28, 2018

Reopening as we explore paths towards TypeScript support. Some initial work has begun in ui-box which will be foundational for adding support in evergreen. 😊

@mshwery mshwery reopened this Dec 28, 2018
@nettofarah
Copy link
Contributor

Here's a PR with type definitions for ui-box: DefinitelyTyped/DefinitelyTyped#31750

I'm suspecting add type defs for evergreen will take a lot more work, so I was hoping we could take kind of a crowdsourced approach.

How do y'all want to go about adding EG types?

@mshwery mshwery added the pinned label Mar 11, 2019
@ChenDogg
Copy link

+1 here...

@mshwery
Copy link
Contributor

mshwery commented May 3, 2019

An update for everyone following this – there are a few moving parts:

  1. We have a WIP branch adding ui-box type declarations: https://github.com/segmentio/ui-box/tree/types/types
  2. We have a WIP branch migrating ui-box fully to TypeScript: https://github.com/segmentio/ui-box/compare/typescript
  3. There's an open branch of Evergreen migrating to TypeScript: [WIP] Migrate to typescript #537

I wanted to outline some goals of these efforts so we can align, decouple risks in such a large effort, and distribute the load a bit. I see 2 primary goals for TypeScript in Evergreen:

Provide type declarations for TypeScript consumers

People who use Evergreen want to be able to do so with complete type-safety in their TypeScript projects.

Reap the benefits of type-safety within Evergreen internals

People who contribute to Evergreen want to be able to do so with all of that amazing tooling around TypeScript. As a side-effect we can achieve the first goal (type declaration) with zero effort by outputting the declarations directly from the source.


I think we can decouple these two goals. It will allow us to immediately ship type declarations while we incrementally migrate to TypeScript within the package. As @paustint aptly said, we can do this by providing a declaration file (.d.ts) in our package distribution and manually keeping it in sync with the source.

I prefer this approach because we can start compiling with TypeScript using our existing Babel setup immediately.

Thoughts?

@mshwery
Copy link
Contributor

mshwery commented May 9, 2019

More updates:

  • ui-box@2.x introduces a complete TypeScript rewrite with type declarations. They are a pretty solid foundation we can build on here. If you see any issues or improvements that could be made to those typedefs, please submit an issue over there.

  • we've begun steady work on migrating parts of evergreen to typescript. we are missing tons of types (lots of explicit any just to get us started). we're targeting the main branch typescript so if you want to contribute to the effort, feel free to open PRs against that branch and slap a "typescript" label to your PR.

  • When we get to a certain level of completion we can start tagging alpha releases for evergreen v5 for testing.

@Rowno Rowno changed the title Support for TS? Migrate to TypeScript Jun 3, 2019
@Rowno Rowno pinned this issue Jun 3, 2019
@robphoenix
Copy link
Contributor

Is there a TODO list to see what needs working on/documenting what's been done?

@mshwery
Copy link
Contributor

mshwery commented Jul 15, 2019

@robphoenix No! This is something we could use help spec'ing out. We are a bit blocked because spread props straight up don't work with TypeScript so anything wrapped in withTheme loses types (which is almost everything)... We've considered moving to context hooks, but we'd lose refs (function components).

See
microsoft/TypeScript#28884
microsoft/TypeScript#28938
microsoft/TypeScript#28748

Off the top of my head here's what we've done:

I guess that leaves:

  • Autocomplete, AutocompleteItem
  • BackButton, Button, IconButton, TextDropdownButton
  • Checkbox
  • Combobox
  • constants/src/Intent, constants/src/Position, constants/src/StackingOrder
  • CornerDialog
  • Dialog
  • FilePicker
  • FormField, FormFieldDescription, FormFieldHint, FormFieldLabel, FormFieldValidationMessage
  • Icon
  • Image
  • Card, Pane
  • Menu, MenuDivider, MenuGroup, MenuItem, MenuOption, MenuOptionsGroup
  • Overlay
  • Popover, PopoverStateless
  • Portal
  • getPosition, Positioner
  • Radio, RadioGroup
  • majorScale, minorScale
  • SearchInput
  • SegmentedControl, SegmentedControlRadio
  • Select, SelectField
  • select-menu: Option, OptionShapePropType, OptionsList, SelectedPropType, SelectMenu, SelectMenuContent
  • SheetClose, SideSheet
  • Spinner
  • ssr: autoHydrate, extractStyles
  • Stack, StackingContext
  • Switch
  • everything in /table/*
  • SidebarTab, Tab, Tablist, TabNavigation
  • Tag, TagInput
  • TextInput, TextInputField
  • Textarea
  • everything in themer/*
  • Toast, Toaster, ToastManager
  • Tooltip, TooltipStateless

@robphoenix
Copy link
Contributor

Grand, thanks for the list @mshwery, I'll have a look at what's been done so far and see if I can help out with some of those components.

We are a bit blocked because spread props straight up don't work with TypeScript so anything wrapped in withTheme loses types (which is almost everything)...

I've only used TS on it's own or with Angular before, just starting to use it with React, so I don't know much about this, I'll read up on it though, thanks for the links.

We've considered moving to context hooks, but we'd lose refs (function components).

Would you mind expanding on this? Do you mean using the useContext hook? How could it work and what refs/function components would you lose?

@mshwery
Copy link
Contributor

mshwery commented Jul 16, 2019

Would you mind expanding on this? Do you mean using the useContext hook? How could it work and what refs/function components would you lose?

@robphoenix yes I'm talking about useContext. Today we wrap a lot of our components in withTheme which is a higher order component that injects the theme context from the nearest ThemeProvider (or default theme context) in the component tree.

useContext would have two main benefits (and 1 drawback):

  1. You don't have to worry about properly typing the higher order component or running into the TypeScript issues I linked above.
  2. The React render tree becomes much simpler to parse (in things like React devtools) because there is far less nesting (which you get from constantly wrapping components in a Context Consumer).

The drawback is that you must use React.FunctionComponents which do not support refs. This is a drawback because we use ref-supporting classes everywhere in evergreen today, so it's entirely possible that consumers use refs directly on our components.

I'm not super opinionated about whether we introduce a breaking change (you cant use refs directly on evergreen components!) or not, but I generally prefer to minimize breaking changes even with major version bumps.

Does that explanation help?

@robphoenix
Copy link
Contributor

@mshwery That explanation helps a lot, thanks for taking the time.

@itsMapleLeaf
Copy link

itsMapleLeaf commented Jul 17, 2019

The drawback is that you must use React.FunctionComponents which do not support refs. This is a drawback because we use ref-supporting classes everywhere in evergreen today, so it's entirely possible that consumers use refs directly on our components.

Have you looked into React.forwardRef and useImperativeHandle?

@mshwery
Copy link
Contributor

mshwery commented Jul 18, 2019

@kingdaro to be honest, no sadly, I haven't spent much time looking into how those would work or how they would be different for consumers who today use ref directly on evergreen components.

@itsMapleLeaf
Copy link

itsMapleLeaf commented Jul 18, 2019

@mshwery Well, these basically bring all of the ref support for function components you could possibly need:

// Do this if you want to directly use an element as a ref
function Input(props: InputProps, ref: React.Ref<HTMLInputElement>) {
  return <input {...props} ref={ref} />
}
// Use uIH if you want to support the old class way of methods on the ref
// This route requires making a custom ref type
export type InputRef = {
  focus: () => void
  inputElement: HTMLInputElement
}

function Input(props: InputProps, ref: React.Ref<InputRef>) {
  const inputRef = useRef<HTMLInputElement>(null)

  useImperativeHandle(ref, () => ({
    focus() {
      if (inputRef.current) inputRef.current.focus()
      // do some special custom action here
    },

    // pass along the input element directly as an escape hatch to the user
    inputElement: inputRef.current,
  }), [])

  return <input {...props} ref={inputRef} />
}
function FormInUserLand() {
  const inputRef = useRef<HTMLInputElement>(null)

  useEffect(() => {
    if (inputRef.current) inputRef.current.focus()
  }, [])

  return <Input ref={inputRef} />
}

Might've made a mistake in there, but hopefully you get the idea. The result for consumers is the same with class components, and backwards compatible

@PabloSzx
Copy link

so there are no TypeScript definitions available at all yet?

@Pasalietis
Copy link
Contributor

Pasalietis commented Sep 11, 2019

Or you can create evergreen-ui/index.d.ts file. For starting you can use my partial file and extend missing definitions.

index.d.ts

/* tslint:disable:interface-name max-classes-per-file no-empty-interface */

declare module 'evergreen-ui' {
  import * as React from 'react';

  type PositionTypes = 'top' | 'top-left' | 'top-right' | 'bottom' | 'bottom-left' | 'bottom-right' | 'left' | 'right';
  type IntentTypes = 'none' | 'success' | 'warning' | 'danger';
  type IconNameTypes =
    ''
    | 'loading'
    | 'error'
    | 'tick-circle'
    | 'caret-down'
    | 'cross'
    | 'menu'
    | 'list'
    | 'more'
    | 'properties'
    | 'diagram-tree'
    | 'database'
    | 'join-table'
    | 'function'
    | 'person'
    | 'import'
    | 'predictive-analysis'
    | 'refresh'
    | 'export';

  interface BoxBackground {
    background?: string;
    backgroundBlendMode?: string;
    backgroundClip?: string;
    backgroundColor?: string;
    backgroundImage?: string;
    backgroundOrigin?: string;
    backgroundPosition?: string;
    backgroundRepeat?: string;
    backgroundSize?: string;
  }

  interface BoxBorderRadius {
    borderBottomLeftRadius?: string | number;
    borderBottomRightRadius?: string | number;
    borderRadius?: string | number;
    borderTopLeftRadius?: string | number;
    borderTopRightRadius?: string | number;
  }

  interface BoxBorders {
    border?: string;
    borderBottom?: string;
    borderBottomColor?: string;
    borderBottomStyle?: string;
    borderBottomWidth?: string | number;
    borderColor?: string;
    borderLeft?: string;
    borderLeftColor?: string;
    borderLeftStyle?: string;
    borderLeftWidth?: string | number;
    borderRight?: string;
    borderRightColor?: string;
    borderRightStyle?: string;
    borderRightWidth?: string | number;
    borderStyle?: string;
    borderTop?: string;
    borderTopColor?: string;
    borderTopStyle?: string;
    borderTopWidth?: string | number;
    borderWidth?: string | number;
  }

  interface BoxShadow {
    boxShadow?: string;
  }

  interface BoxDimensions {
    height?: string | number;
    maxHeight?: string | number;
    maxWidth?: string | number;
    minHeight?: string | number;
    minWidth?: string | number;
    width?: string | number;
  }

  interface BoxFlex {
    alignContent?: string;
    alignItems?: string;
    alignSelf?: string;
    flex?: string | number;
    flexBasis?: string | number;
    flexDirection?: string;
    flexFlow?: string;
    flexGrow?: string | number;
    flexShrink?: string | number;
    flexWrap?: string;
    justifyContent?: string;
    justifyItems?: string;
    justifySelf?: string;
    order?: string | number;
    placeContent?: string;
    placeItems?: string;
    placeSelf?: string;
  }

  interface BoxGrid {
    columnGap?: string | number;
    gap?: string | number;
    grid?: string;
    gridArea?: string;
    gridAutoColumns?: string | number;
    gridAutoFlow?: string;
    gridAutoRows?: string | number;
    gridColumn?: string | number;
    gridColumnEnd?: string | number;
    gridColumnGap?: string | number;
    gridColumnStart?: string | number;
    gridGap?: string | number;
    gridRow?: string | number;
    gridRowEnd?: string | number;
    gridRowGap?: string | number;
    gridRowStart?: string | number;
    gridTemplate?: string;
    gridTemplateAreas?: string;
    gridTemplateColumns?: string;
    gridTemplateRows?: string;
    rowGap?: string | number;
  }

  interface BoxInteraction {
    cursor?: string;
    pointerEvents?: string;
    userSelect?: string;
    visibility?: string;
  }

  interface BoxLayout {
    boxSizing?: string;
    clear?: string;
    clearfix?: boolean;
    display?: 'block'
      | 'contents'
      | 'flex'
      | 'grid'
      | 'inherit'
      | 'initial'
      | 'inline'
      | 'inline-block'
      | 'inline-flex'
      | 'inline-grid'
      | 'inline-table'
      | 'list-item'
      | 'none'
      | 'table'
      | 'table-caption'
      | 'table-cell'
      | 'table-columnr'
      | 'table-column-group'
      | 'table-header-group'
      | 'table-footer-group'
      | 'table-row'
      | 'table-row-group'
      | '';
    float?: string;
    zIndex?: number;
  }

  interface BoxList {
    listStyle?: string;
    listStyleType?: string;
    listStyleImage?: string;
    listStylePosition?: string;
  }

  interface BoxOpacity {
    opacity?: string | number;
  }

  interface BoxOverflow {
    overflow?: 'auto' | 'hidden' | 'inherit' | 'initial' | 'revert' | 'scroll' | 'unset' | 'visible';
    overflowX?: 'auto' | 'hidden' | 'inherit' | 'initial' | 'revert' | 'scroll' | 'unset' | 'visible';
    overflowY?: 'auto' | 'hidden' | 'inherit' | 'initial' | 'revert' | 'scroll' | 'unset' | 'visible';
  }

  interface BoxPosition {
    bottom?: string | number;
    left?: string | number;
    position?: 'absolute' | 'fixed' | 'inherit' | 'initial' | 'relative' | 'revert' | 'static' | 'sticky' | 'unset';
    right?: string | number;
    top?: string | number;
  }

  interface BoxSpacing {
    margin?: string | number;
    marginBottom?: string | number;
    marginLeft?: string | number;
    marginRight?: string | number;
    marginTop?: string | number;
    marginX?: string | number;
    marginY?: string | number;
    padding?: string | number;
    paddingBottom?: string | number;
    paddingLeft?: string | number;
    paddingRight?: string | number;
    paddingTop?: string | number;
    paddingX?: string | number;
    paddingY?: string | number;
  }

  interface BoxText {
    color?: string;
    font?: string;
    fontFamily?: string;
    fontSize?: string | number;
    fontStyle?: string;
    fontVariant?: string;
    fontWeight?: string | number;
    letterSpacing?: string | number;
    lineHeight?: string | number;
    textAlign?: 'center' | 'end' | 'inherit' | 'justify' | 'left' | 'match-parent' | 'revert' | 'right' | 'start' | 'unset';
    textDecoration?: string;
    textOverflow?: string;
    textShadow?: string;
    textTransform?: string;
    whiteSpace?: string;
    wordBreak?: string;
    wordWrap?: string;
  }

  interface BoxTransform {
    transform?: string;
    transformOrigin?: string;
  }

  interface BoxTransition {
    transition?: string;
    transitionDelay?: string;
    transitionDuration?: string;
    transitionProperty?: string;
    transitionTimingFunction?: string;
  }

  interface UiBoxPropsType extends BoxBackground,
    BoxBorderRadius,
    BoxBorders,
    BoxShadow,
    BoxDimensions,
    BoxFlex,
    BoxGrid,
    BoxInteraction,
    BoxLayout,
    BoxList,
    BoxOpacity,
    BoxOverflow,
    BoxPosition,
    BoxSpacing,
    BoxText,
    BoxTransform,
    BoxTransition {
    is?: React.ReactNode;
    to?: string;
    css?: object;
    style?: object;
    innerRef?: (ref: HTMLElement) => void;
    onMouseDown?: (e: React.MouseEvent<HTMLInputElement>) => void;
    onMouseUp?: (e: React.MouseEvent<HTMLInputElement>) => void;
  }

  export interface AlertProps extends BoxDimensions, BoxLayout, BoxPosition, BoxSpacing {
    intent: IntentTypes;
    title?: React.ReactNode;
    hasTrim?: boolean;
    hasIcon?: boolean;
    isRemoveable?: boolean;
    onRemove?: () => void;
    appearance?: 'default' | 'card';
    children?: React.ReactNode;
  }

  export class Alert extends React.PureComponent<AlertProps> {
  }

  // https://github.com/downshift-js/downshift
  export interface AutocompleteProps {
    title?: React.ReactNode;
    items: any[];
    itemToString?: (i: any) => string;
    children: (props: {
                 toggle: () => void,
                 getRef: (ref: React.RefObject) => void,
                 isShown: NonNullable<PopoverProps['isShown']>,
                 getInputProps: () => {
                   onKeyDown: (e: React.ChangeEvent<any>) => void;
                   onChange: (e: React.ChangeEvent<any>) => void;
                   onBlur: (e: React.ChangeEvent<any>) => void;
                 },
                 openMenu: () => any,
                 inputValue: string,
               },
    ) => React.ReactNode;
    itemSize?: number;
    position?: PositionTypes;
    isFilterDisabled?: boolean;
    popoverMinWidth?: number;
    popoverMaxHeight?: number;
    selectedItem?: any;
    buttonProps?: buttonProps;
    onChange: (selectedItem: any) => void;
  }

  export class Autocomplete extends React.PureComponent<AutocompleteProps> {
  }

  export interface AvatarProps {
    src?: string;
    size?: number;
    name?: string;
    hashValue?: string;
    isSolid?: boolean;
    color?: string;
    getInitials?: (name: string) => string;
    forceShowInitials?: boolean;
    sizeLimitOneCharacter?: number;
  }

  export class Avatar extends React.PureComponent<AvatarProps> {
  }

  export interface CheckboxProps extends BoxDimensions, BoxLayout, BoxPosition, BoxSpacing, TextProps {
    id?: string;
    name?: string;
    label?: React.ReactNode;
    value?: string;
    checked?: boolean;
    indeterminate?: boolean;
    onChange?: (e: React.ChangeEvent<string>) => void;
    disabled?: boolean;
    isInvalid?: boolean;
    appearance?: 'default';
  }

  export class Checkbox extends React.PureComponent<CheckboxProps> {
  }

  export interface ButtonProps extends BoxDimensions, BoxLayout, BoxPosition, BoxSpacing, TextProps {
    type?: 'submit' | 'button';
    intent?: IntentTypes;
    appearance?: 'default' | 'minimal' | 'primary';
    isLoading?: boolean;
    isActive?: boolean;
    iconBefore?: IconNameTypes;
    iconAfter?: IconNameTypes;
    disabled?: boolean;
    className?: string;
    onClick?: (e: React.ChangeEvent<any>) => void | false | undefined;
  }

  export class Button extends React.PureComponent<ButtonProps> {
  }

  export class Card extends React.PureComponent<PaneProps> {
  }

  export interface IconProps {
    color?: string;
    icon: IconNameTypes;
    size?: number;
    title?: string;
    style?: Record<string, string | number>;
  }

  export class Icon extends React.PureComponent<IconProps> {
  }

  export interface FormFieldProps extends BoxDimensions, BoxSpacing, BoxPosition, BoxLayout {
    label: NonNullable<React.ReactNode>;
    labelFor?: string;
    description?: React.ReactNode;
    hint?: React.ReactNode;
    validationMessage?: React.ReactNode;
  }

  export class FormField extends React.PureComponent<FormFieldProps> {
  }

  export class FormFieldDescription extends React.PureComponent<ParagraphProps> {
  }

  export class FormFieldHint extends React.PureComponent<ParagraphProps> {
  }

  export interface FormFieldLabelProps extends LabelProps {
    isAstrixShown?: boolean;
  }

  export class FormFieldLabel extends React.PureComponent<FormFieldLabelProps> {
  }

  export interface FormFieldValidationMessageProps extends PaneProps {
    children?: React.ReactNode;
  }

  export class FormFieldValidationMessage extends React.PureComponent<FormFieldValidationMessageProps> {
  }

  export interface IconButtonProps extends ButtonProps {
    icon: IconNameTypes;
    iconAim?: 'down' | 'up';
    iconSize?: number;
  }

  export class IconButton extends React.PureComponent<IconButtonProps> {
  }

  export interface LabelProps extends TextProps {
    htmlFor?: string;
    className?: string;
  }

  export class Label extends React.PureComponent<LabelProps> {
  }

  export interface MenuProps {
    children: React.ReactNode[];
  }

  export interface MenuItemProps {
    is?: string | (() => void);
    onSelect?: () => void;
    icon?: React.JSX;
    children?: React.JSX;
    secondaryText?: React.JSX;
    appearance?: 'default';
    intent?: IntentTypes;
  }

  export class Menu extends React.PureComponent<MenuProps> {
    public static Item = class MenuItem extends React.PureComponent<MenuItemProps> {
    };
    public static Divider = class MenuDivider extends React.PureComponent {
    };
  }

  export interface PaneProps extends UiBoxPropsType {
    background?: 'tint1' | 'tint2' | 'overlay' | 'yellowTint' | 'greenTint' | 'orangeTint' | 'redTint' | 'blueTint' | 'purpleTint' | 'tealTint';
    elevation?: 0 | 1 | 2 | 3 | 4;
    hoverElevation?: 0 | 1 | 2 | 3 | 4;
    activeElevation?: 0 | 1 | 2 | 3 | 4;
    border?: string | boolean;
    borderTop?: string | boolean;
    borderRight?: string | boolean;
    borderBottom?: string | boolean;
    borderLeft?: string | boolean;
  }

  export class Pane extends React.PureComponent<PaneProps> {
  }

  export interface PopoverProps {
    position?: PositionTypes;
    isShown?: boolean;
    trigger?: 'click' | 'hover';
    content: React.ReactNode | ((object: { close: () => void }) => React.ReactNode);
    children:
      ((props: { toggle: () => void, getRef: (ref: React.RefObject) => void, isShow: NonNullable<PopoverProps['isShown']> }) => React.ReactNode)
      | React.ReactNode;
    display?: string;
    minWidth?: number | string;
    minHeight?: number | string;
    animationDuration?: number;
    onOpen?: () => void;
    onClose?: () => void;
    onOpenComplete?: () => void;
    onCloseComplete?: () => void;
    onBodyClick?: () => void;
    bringFocusInside?: boolean;
    shouldCloseOnExternalClick?: boolean;
  }

  export class Popover extends React.PureComponent<PopoverProps> {
  }

  export interface ParagraphProps extends UiBoxPropsType {
    size?: 300 | 400 | 500;
    fontFamily?: 'ui' | 'display' | 'mono';
  }

  export class Paragraph extends React.PureComponent<ParagraphProps> {
  }

  export interface PositionerProps {
    position?: PositionTypes;
    isShown?: boolean;

    children: (params: {
      top: number,
      left: number,
      zIndex: NonNullable<StackProps['value']>,
      css,
      style: {
        transformOrigin: string,
        left: number,
        top: number,
        zIndex: NonNullable<StackProps['value']>,
      },
      getRef: (ref: React.RefObject) => void,
      animationDuration: PositionerProps['animationDuration'],
      state: 'exited' | 'entering' | 'entered' | 'exiting';
    }) => React.ReactNode;
    innerRef?: (ref: React.RefObject) => void;
    bodyOffset?: number;
    targetOffset?: number;
    target: (params: { getRef: () => React.RefObject, isShow: boolean }) => React.ReactNode;
    initialScale?: number;
    animationDuration?: number;
    onCloseComplete?: () => void;
    onOpenComplete?: () => void;
  }

  export class Positioner extends React.PureComponent<PositionerProps> {
  }

  export interface RadioProps extends BoxSpacing, BoxPosition, BoxLayout, BoxDimensions {
    id?: string;
    name?: string;
    label?: React.ReactNode;
    value?: string;
    onChange?: (e: React.ChangeEvent<any>) => void;
    disabled?: boolean;
    checked?: boolean;
    size?: 12 | 16;
    isRequired?: boolean;
    isInvalid?: boolean;
    appearance?: 'default';
  }

  export class Radio extends React.PureComponent<RadioProps> {
  }

  export interface RadioGroupProps extends BoxSpacing, BoxPosition, BoxLayout, BoxDimensions {
    options: Array<{ label: React.ReactNode, value: string, isDisabled?: boolean }>;
    value?: string;
    defaultValue?: string;
    onChange: (value: string) => void;
    label?: string;
    size?: 12 | 16;
    isRequired?: boolean;
  }

  export class RadioGroup extends React.PureComponent<RadioGroupProps> {
  }

  export class SearchInput extends React.PureComponent<TextInputProps> {
  }

  export interface SegmentedControlProps extends BoxSpacing, BoxPosition, BoxLayout, BoxDimensions {
    options: Array<{ label: string, value: NonNullable<SegmentedControlProps['value']> }>;
    value?: number | string | boolean;
    defaultValue?: number | string | boolean;
    onChange: (value: NonNullable<SegmentedControlProps['value']>) => void;
    name?: string;
    height?: number;
  }

  export class SegmentedControl extends React.PureComponent<SegmentedControlProps> {
  }

  export interface SelectMenuProps {
    title?: string;
    width?: string | number;
    height?: string | number;
    options: Array<{ label: string, value: string | null }>;
    onSelect?: (item: { label: string, value: string }) => void;
    onDeselect?: (item: { label: string, value: string }) => void;
    selected?: string | string[];
    isMultiSelect?: boolean;
    hasTitle?: boolean;
    hasFilter?: boolean;
    filterPlaceholder?: string;
    filterIcon?: IconNameTypes;
    onFilterChange?: (searchValue: string) => void;
    position?: Omit<PositionTypes, 'left' | 'right'>;
    detailView?: PopoverProps['content'];
    titleView?: React.ReactNode | (() => React.ReactNode);
    emptyView?: React.ReactNode | (() => React.ReactNode);
    closeOnSelect?: boolean;
  }

  export class SelectMenu extends React.PureComponent<SelectMenuProps> {
  }

  export interface SideSheetProps {
    children: React.ReactNode | (() => React.ReactNode);
    isShown?: boolean;
    onCloseComplete?: () => void;
    onOpenComplete?: () => void;
    onBeforeClose?: () => void;
    shouldCloseOnOverlayClick?: boolean;
    shouldCloseOnEscapePress?: boolean;
    width?: string | number;
    containerProps?: PaneProps;
    position?: PICK<PositionTypes, 'top' | 'bottom' | 'left' | 'right'>;
    preventBodyScrolling?: boolean;
  }

  export class SideSheet extends React.PureComponent<SideSheetProps> {
  }

  export interface SidebarTabProps extends TabProps {
  }

  export class SidebarTab extends React.PureComponent<SidebarTabProps> {
  }

  export interface StackProps {
    children: (zIndex: number) => React.ReactNode;
    value?: number;
  }

  export class Stack extends React.PureComponent<StackProps> {
  }

  export interface TabProps extends TextProps {
    onSelect?: () => void;
    isSelected?: boolean;
    disabled?: boolean;
    appearance?: 'default';
  }

  export class Tab extends React.PureComponent<TabProps> {
  }

  export interface TablistProps extends UiBoxPropsType {
  }

  export class Tablist extends React.PureComponent<TablistProps> {
  }

  export interface TabNavigationProps extends UiBoxPropsType {
  }

  export class TabNavigation extends React.PureComponent<TabNavigationProps> {
  }

  export interface TextProps extends UiBoxPropsType {
    size?: 300 | 400 | 500 | 600;
    fontFamily?: 'ui' | 'display' | 'mono';
  }

  export class Text extends React.PureComponent<TextProps> {
  }

  export interface TextInputProps extends TextProps {
    id?: string;
    name?: string;
    type?: 'text' | 'number' | 'hidden';
    required?: boolean;
    disabled?: boolean;
    readOnly?: boolean;
    isInvalid?: boolean;
    spellCheck?: boolean;
    placeholder?: string;
    appearance?: 'default' | 'primary';
    width?: string | number;
    className?: string;
    value?: string | number;
    onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
    onKeyUp?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
    onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
    onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
    onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
    min?: string;
    max?: string;
  }

  export class TextInput extends React.PureComponent<TextInputProps> {
  }

  export interface TextInputFieldProps extends TextInputProps, FormFieldProps {
    inputHeight?: number;
    inputWidth?: number | string;
  }

  export class TextInputField extends React.PureComponent<TextInputFieldProps> {
  }

  export interface TooltipProps extends TextProps {
    appearance?: 'default' | 'card';
    position?: PositionTypes;
    content: React.ReactNode;
    hideDelay?: number;
    isShown?: boolean;
    children: React.ReactNode;
  }

  export class Tooltip extends React.PureComponent<TooltipProps> {
  }

  export interface SpinnerProps extends UiBoxPropsType {
    delay?: number;
    size: number;
  }

  export class Spinner extends React.PureComponent<SpinnerProps> {
  }
}

@blujedis
Copy link

@Pasalietis you ought to push that up to a repo. We don't use anything without TS anymore. It's must, particularly on larger projects. That said I've liked Evergreen for a very long time, as mentioned the lack of types unfortunately is a deal breaker for our core stuff. Great start though and thx for sharing!!

@itsMapleLeaf

This comment has been minimized.

@seanemmer
Copy link

seanemmer commented Oct 3, 2019

Can we please get some guidance as to whether a Typescript rewrite and/or definitions are forthcoming from the maintainers?

If one is not forthcoming, can we at least get type definitions pushed to DefinitelyTyped. @Pasalietis has put together a great start above and it is poor practice to manually copy that into each project, rather have the community maintain the definitions in a central repo.

@clarkkozak
Copy link

@Pasalietis I'm still learning TypeScript and having a hard time adding the Heading Component types.

For those who are using this in a project, this is what I'm doing in my package.json

  "scripts": {
...
    "postinstall": "wget https://gist.githubusercontent.com/juxley/e516412bdfe9fde58acb082248238271/raw/c0f0783c6a78fb37854408aef19198b159a93044/index.d.ts && mv index.d.ts node_modules/evergreen-ui"
...
  },

@blujedis
Copy link

blujedis commented Oct 3, 2019

@Juxley typically the way to do it is to add a new type root in your tsconfig.json then have a type root in your projects named "types".

So it would be:

/types
/evergreen-ui
index.d.ts

Not that what you're doing won't work but you may wan to investigate "type roots" in tsconfig referencing the above. Hope that helps.

@clarkkozak
Copy link

@blujedis I don't follow unfortunately, yet now the types for the Heading components works! :D

The way I'm doing it is definitely not the right way...yet it's the way that's currently working until Evergreen supports types...

@mshwery
Copy link
Contributor

mshwery commented Oct 3, 2019

@seanemmer @Pasalietis I would personally prefer to avoid putting it in DefinitelyTyped because the process to get that merged, released, updated can be fairly slow. That said, we haven't made enough meaningful progress to migrate to TypeScript. We have work started in the typescript branch and have added lots of types, but there are still a lot of missing pieces.

I'm thinking it would make sense to add a .d.ts file and distribute that in the package.json in the meantime, while we continue to move towards a 100% TypeScript evergreen-ui.

The other challenge is keeping up with drift as new changes make their way into master. At this point I'd prefer distributing a typedef file directly in evergreen. We have a lot of types internally defined as well, just not included in the repo yet (was experimental). We should combine our efforts with @Pasalietis' work!

@blujedis
Copy link

blujedis commented Oct 3, 2019

@mshwery DefinitelyTyped excuse the pun is definitely not the answer. I would also arg that the proj should be natively rewritten in TS (apologies if this is already in works or pipeline). It isn't that difficult and doesn't have to be done all at once. You can get it spun up and let the comm keep contributing. Meaning you can leave many types as "any" until done and then do an official release. If you look at it as well we can't release until we have all types or have it all refactored it may really slow things. We do a ton of TS and really like EG but for our larger proj it's just not possible for us to use without TS. I sincerely believe if EG were native TS it would take off like wildfire. Commit to rewrite in TS I'll pledge one weekend a month to contrib. Just my .02

@mshwery
Copy link
Contributor

mshwery commented Oct 3, 2019

We have work started in the typescript branch and have added lots of types, but there are still a lot of missing pieces.

@blujedis see https://github.com/segmentio/evergreen/compare/typescript?expand=1 :)

@NEO97online
Copy link
Contributor

@mshwery Are you still blocked on the ref issue mentioned here? #300 (comment)

There are ways to use refs in function components now, so maybe a good starting point is migrating to context hooks. What do you think?

@mshwery
Copy link
Contributor

mshwery commented Oct 3, 2019

@auderer yeah I haven't taken a look at implementing that myself so that would definitely help! The ...props issue with TypeScript not properly typing things in the higher order component makes it really hard for us to use the withTheme HOC without losing typedefs. If we can safely support refs and move to useContext instead for the theme provider, that would be amazing!

Additionally it would help trim down the React tree representation in devtools :)

@blujedis
Copy link

blujedis commented Oct 3, 2019

@mshwery done a lot with that recently actually (Context, useImperativeHandle etc) . If still stale in about 10 days or there about I can take a look.

@seanemmer
Copy link

seanemmer commented Oct 3, 2019

@mshwery adding a .d.ts file seems like a reasonable stopgap given the constraints and uncertain timing around a full-fledged rewrite. Should @Pasalietis make a PR with their progress so far? The community can then contribute updates (e.g. I just noticed Table is not covered) and attempt to manage the drift you mention.

@mshwery
Copy link
Contributor

mshwery commented Oct 3, 2019

@mshwery adding a .d.ts file seems like a reasonable stopgap given the constraints and uncertain timing around a full-fledged rewrite. Should @Pasalietis make a PR with their progress so far? The community can then contribute updates (e.g. I just noticed Table is not covered) and attempt to manage the drift you mention.

@seanemmer Yeah adding a PR with that .d.ts file might be a good first step. We can start layering in our own internal types where they differ, in follow-up PRs.

@seanemmer
Copy link

@Pasalietis are you available to make the PR?

@Pasalietis
Copy link
Contributor

@seanemmer I don't think it is suitable for PR, because it's lacking some types. I could create PR with missing components with all props as any, but it could still have some typing bugs.

@seanemmer
Copy link

@Pasalietis I think missing types is better than no types - I've been using it as is and it's pretty easy to ts-ignore imports for untyped components. The community can then jump in and start populating the missing types.

@mshwery what do you think?

@Pasalietis
Copy link
Contributor

I create PR #646 with update types, but at this moment it can't be merged, because it depends on ui-box@^2.0.0

@mshwery
Copy link
Contributor

mshwery commented Oct 30, 2019

Alright, getting a bunch more types added here – #660

Happy to release this as-is, and we can improve upon the types (and documentation)

@mshwery
Copy link
Contributor

mshwery commented Oct 31, 2019

https://github.com/segmentio/evergreen/releases/tag/v4.21.0

Not 100% coverage, but this should be a healthy starting point! Closing this issue for now and we can track for an actual code migration via new issues/prs

@mshwery mshwery closed this as completed Oct 31, 2019
@mshwery mshwery unpinned this issue Oct 31, 2019
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