Giter Club home page Giter Club logo

hearth's Introduction

Hearth

A simple starter kit for the Laravel framework.

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Code coverage status Localization status Total Downloads


Hearth is a simple starter kit for the Laravel framework. It provides a few things out of the box:

  • A user model with login, registration, email verification
  • Optional two-factor authentication support for users.
  • An organization model.
  • A membership model which reflects users' roles within organizations.
  • An invitation model which allows users to be invited to join organizations.
  • A resource model supporting creation of and access to a library of open educational resources in a wide range of formats.
  • Multilingual support, including localized routes.
  • Bare bones views which can be published and customized as needed.

Installation

Hearth requires PHP >= 8.1 and Laravel >= 10.

You may use Composer to install Hearth into your new Laravel project:

composer require fluid-project/hearth

Note: attempting to install Hearth into an existing Laravel application will result in unexpected behaviour.

After installing the Hearth package, you can use the hearth:install Artisan command to install the Hearth scaffolding within your Laravel application:

php artisan hearth:install

After installing Hearth, you will need to install and build your NPM dependencies, run your database migrations and link public storage:

npm install
php artisan migrate
php artisan storage:link

Emails

In order to test emails (for example, using Mailhog with Laravel Sail), you must update your Laravel application's .env file's MAIL_FROM_ADDRESS environment variable with a properly-formatted email address. For local development, this might be [email protected] (assuming your local application is accessible at http://hearth.test).

Usage

TODO.

Formatting

To format your code using Laravel Pint, you can run:

composer format

This should be done prior to each commit, or at least prior to opening a pull request.

Testing

Prior to testing, you will need to create a MySQL or MariaDB database for testing with credentials which match those in phpunit.xml.dist. Then run:

composer test

You can get code coverage results if XDebug is installed by running:

composer test-coverage

To test the code located in the stubs directory you'll need to install Hearth into a Laravel instance and run the tests from there.

Analysis/Linting

The code should pass level 5 testing.

composer analyze

To analyze the code located in the stubs directory you'll need to install Hearth into a Laravel instance and run the analysis from there. The phpstan configuration is provided as part of the install, but Larastan will need to be manually installed.

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The BSD 3-Clause License. Please see License File for more information.

Third Party Software in Hearth

Hearth is based on other publicly available software, categorized by license:

MIT License

hearth's People

Contributors

chosww avatar dependabot[bot] avatar github-actions[bot] avatar greatislander avatar jobara avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hearth's Issues

Validation Error handling does not work when validating arrays of values

Describe the bug

The HandlesValidation trait doesn't handle validation of arrays. When validating an array value, the key is returned with the array index that failed validation appended to the end. So for example if the form input name is toValidate and the first time fails validation the error key is returned as toValidate.0

To reproduce

Steps to reproduce the behavior:

  1. use a component like <x-hearth-checkboxes name="toValidate" />
  2. validate the input of the array of values by having a validation rule like ['toValidate.*' => ['min:3']; which would check each value in the array to ensure it passes the validation rule. In real world cases this may be more likely to occur with enum validation.
  3. Notice that values that fail are returned in the error bag keyed off of the name with the * replaced by the index. For example, toValidate.0.

Expected behavior

hasErrors will return true for arrays that fail validation. Allowing the error <x-hearth-error /> to be displayed and linked appropriately. For consistency with the radio-buttons should probably also mark the checkbox inputs with aria-invalid.

Additional context or notes

Accessible validation of checkbox and radiobutton groups suggests not marking all inputs in a group with aria-invalid. However, the article is a bit old, so not sure it's the best current advice.

Add index, show, create, edit, and delete controller, routes, and views for ResourceCollection model

Is your feature request related to a problem?

We need a controller, routes and views which allow an authenticated user to list, view, create, edit, and delete a ResourceCollection.

Describe the solution you'd like

Add routes, controllers, and views to support this functionality. The Resource model's views, controller, and routes can be used as an example.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Form actions should use Form Requests (e.g. StoreResourceCollectionRequest and UpdateResourceCollectionRequest for persisting the model to the database and updating the model in storage).

No matter what locale is selected from the locale switcher, settings page always redirects to user's saved locale

Describe the bug

While on the user settings page, choosing a different locale from the locale switcher in the primary navigation doesn't change the interface to that language as the user is automatically redirected to their stored locale.

To reproduce

Steps to reproduce the behavior:

  1. Create an account.
  2. Navigate to the user settings page.
  3. Using the locale switcher in the primary navigation, select "Français".
  4. Observe that the page reloads but remains in English.

Expected behavior

The page should switch to French.

Additional context or notes

If a user has a stored preference but switches locales from the primary menu, should their preference be used for subsequent page loads? Or should switching locales from the primary menu update their preference?

Locale full names should display consistently

Noticed when testing - I suspect this may be a function of the library we're using to generate locales and it might be arguably grammatical in some sense of the locale/language's own rules as to whether or not it should be capitalized, but we should probably normalize the display in our generated components in some way.

image

Screenshot description: a language selector showing:
* English
* Faroese
* français
* Deutsch

Date input doesn't properly reflect error state

Describe the bug

When the hidden input that's part of the date input component is in an error state, the whole component needs to reflect it. It doesn't right now.

To reproduce

Steps to reproduce the behavior:

  1. Don't fill out a required date input.
  2. Submit the form.

Expected behavior

The date input fields should reflect an error state and the message should appear at the bottom of the component.

Screenshots

Not available.

Technical details

All platforms.

Additional context or notes

Not applicable.

Explicit yes/no confirmation for locale selection

Currently, the interactive locale selection process during installation treats any response other than explicit y/es as a no, and finishes the install process.

It would be better to have the user explicitly confirm that no locales / no further locales are required before finishing the install process.

Add a postinstall script so that `npm run dev` doesn't have to triggered manually after install

Is your feature request related to a problem?

Not really a problem, but it would be simpler if all of the npm installation process could happen with a single command.

currently the instructions say:

composer require fluid-project/hearth

php artisan hearth:install

npm install
npm run dev

Describe the solution you'd like

Using a postinstall script would trigger the current development script to run automatically after npm install. A further step would be to have composer post install script kick off the npm install too.

Describe alternative solutions you've considered

The alternative would be to just leave things as is.

Cannot disable hearth checkboxes component

Describe the bug

The checkboxes component does not pass down the disabled property to the individual checkbox inputs. It would probably be good to take in disabled which would disable all checkboxes or an array which checkboxes should be disabled; similar to how checked works.

To reproduce

Steps to reproduce the behavior:

  1. create a checkboxes component with disabled <x-hearth-checkboxes disabled />
  2. None of the checkboxes are disabled

Expected behavior

The checkboxes should be disabled

Add ResourceCollection model

Is your feature request related to a problem?

For Hearth's resource library functionality, we need the ability for users to create collections of thematically related resources. This functionality is distinct from the ability to tag resources with topics, types of resource, etc. and filtering by these attributes. Resources collections will be manually curated and may contain resources that are a mix of topics/types/etc. but that are somehow related.

Describe the solution you'd like

We should introduce a new model called ResourceCollection with an appropriate relationship to the existing Resource model. Each resource collection should also include translatable title and description fields and should belong to the User who created it.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

How to add database migrations to a Laravel package like Hearth:

See rough implementation of this here:

Relevant documentation on relationships in Laravel:

Articles on database design:

Add Larastan configuration and ensure that all stubbed files pass analysis

Is your feature request related to a problem?

Psalm does not support Laravel 9 yet. Making Larastan the likely option for static analysis with a Hearth based project using Laravel 9.

Describe the solution you'd like

Add default Larastan configuration and ensure that all stubbed files pass the analysis.

Describe alternative solutions you've considered

psalm-plugin-laravel, but it doesn't support Laravel 9.
see: psalm/psalm-plugin-laravel#213

Additional context or notes

Currently when trying to Larastan with the default configuration it throws errors such as:

  • Line 37: Http/Requests/UpdateOrganizationRequest.php -
    • Cannot access property $id on object|string.
  • Line 26: Models/Organization.php
    • PHPDoc type array of property App\Models\Organization::$fillable is not covariant with PHPDoc type array of overridden property

Can look at accessibility-exchange for inspiration on what might need to be updated and how to address the linting issues.

Deleting a user doesn't check if they are the sole admin of an organization

Describe the bug

The organization interface prevents downgrading or removing the only admin in an organization. However, if the admin user deletes their account, there is no error checking regarding admin membership in organizations. The organization will be left in an uneditable state.

To reproduce

Steps to reproduce the behavior:

  1. Create a new user account
  2. Create a new organization with this user
  3. Invite other users as members to the organization
  4. Delete the admin user's account
  5. The organization is no longer editable as there is no admin

Expected behavior

The user should be required to promote at least one more admin into each organization, or delete the organizations, before their account can be deleted. For example this could be accomplished through a warning, or some actions facilitated through the deletion process.

Related fields should be able to reference a shared hint in their aria-describedby attribute

Is your feature request related to a problem?

Currently, Hearth provides some generic components which help with constructing form inputs:

  • <x-hearth-input>
  • <x-hearth-textarea>
  • <x-hearth-select>

(Going forward, I'll use <x-hearth-input> as a generic example as this issue is shared by all three of these components.)

If an <x-hearth-input> has the hinted attribute, its aria-describedby attribute will be populated with a reference to an id for the hint in the form <name>-hint (where <name> is the name attribute of the input). The hint is generated using an <x-hearth-hint> component with a for attribute referencing the name / id of the input. Furthermore, if the input has failed validation, aria-describedby will also contain an id reference for an error with an id in the form of in the form <name>-error (where <name> is the name attribute of the input). Any errors can be generated using an <x-hearth-error> component with a for attribute referencing the name / id of the input and, optionally, a bag attribute referencing a named error bag. Here's a complete example:

<x-hearth-label for="fullname">Your name</x-hearth-label>
<x-hearth-input name="fullname" type="text" hinted />
<x-hearth-hint for="fullname">Enter your first and last name.</x-hearth-hint>
<x-hearth-error for="fullname" />

If the input is valid, this compiles to:

<label for="name">Your name</label>
<input name="name" id="name" type="text" aria-describedby="name-hint">
<p class="field__hint" id="name-hint">Enter your first and last name.</p>

If the input is not valid, this compiles to:

<label for="name">Your name</label>
<input name="name" id="name" type="text" aria-describedby="name-hint name-error" aria-invalid="true">
<p class="field__hint" id="name-hint">Enter your first and last name.</p>
<p class="field__error" id="name-error">This field is required.</p>

The problem arises when two related fields need to share a hint. For example, inputs for city/town and province/territory might have a shared hint which reads:

Providing your location will allow us to show you local content.

There's currently no way to pass a reference to a specific hint to more than one input component.

Describe the solution you'd like

The hinted attribute should accept an optional value equal to the id of the hint. So, in the example above, one could do the following:

<x-hearth-hint for="address">Providing your location will allow us to show you local content.</x-hearth-hint>
<x-hearth-label for="locality">City or town</x-hearth-label>
<x-hearth-input name="locality" type="text" hinted="address-hint" />
<x-hearth-error for="locality" />
<x-hearth-label for="region">Province or territory</x-hearth-label>
<x-hearth-input name="region" type="text" hinted="address-hint" />
<x-hearth-error for="region" />

This would compile to:

<p class="field__hint" id="address-hint">Providing your location will allow us to show you local content.</p>
<label for="locality">City or town</label>
<input name="locality" id="locality" type="text" value="" aria-describedby="address-hint">
<label for="region">Province or territory</label>
<input name="region" id="region" type="text" value="" aria-describedby="address-hint">

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Not applicable.

Remove Button component

Is your feature request related to a problem?

As identified in #122 (comment), the Button component as implemented offers no advantages over a standard button (it might if utility class-based styling such as Tailwind were used — see here — but it isn't).

Describe the solution you'd like

The Button component should be fully removed in v3.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Not applicable.

radio buttons and checkboxes components generate invalid ids if the values contain spaces

Describe the bug

If the options for generating a set of checkboxes or radio buttons include values with spaces, the IDs are generated with spaces which is not valid.

To reproduce

Add an <x-hearth-radio-buttons /> or <x-hearth-checkboxes /> component with options such as:

[
    'opt 1' => 'first option',
    'opt 2' => 'second option',
]

The resulting html with have something like  `name="name" value="opt 1" id=name-opt 1"`

Expected behavior

I could be wrong, but I believe spaces are valid in the value attribute. If that is the case, the IDs should be generated in a valid way, perhaps converting them to a slug format.

Add multilingual slug support

Is your feature request related to a problem?

See: chinleung/laravel-multilingual-routes#70

Describe the solution you'd like

  • Ensure that the service provider from Laravel Multilingual Routes is inserted at the beginning of the web group.
  • Provide our own helper to retrieve the current route in another language.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Not applicable.

signing in after deleting an account throws an error

Describe the bug

After successfully deleting an account, trying to sign in again with the another valid account throws an error.

Symfony\Component\Routing\Exception\RouteNotFoundException
Route [login] not defined. 

Illuminate\Routing\UrlGenerator::route
vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php:444

To reproduce

Steps to reproduce the behavior:

  1. Add two accounts and assign them both as admins to an organization
  2. Delete one of the accounts
  3. Click on "Sign in" and attempt to sign in with the other account.
  4. The error page appears

Expected behavior

The account should login as expected.

Screenshots

Screen Shot 2021-09-08 at 3 44 42 PM

provide a way of linking to checkbox input fields

Is your feature request related to a problem?

When an error occurs, it is possible to create a summary at the top of the page listing all of the validation errors. When this method is used, it is helpful to provide direct links to the inputs with errors. Laravel's error array is keyed off of the inputs name. Because most hearth components also use the value of the name for their id it is easy to setup an anchor like #name to link to the related field. However, with <x-hearth-checkboxes /> the ids are suffixed with the input's value and the name field is suffixed with [].

Describe the solution you'd like

The first input could be generated without the value appended to the end, so that an anchor could be generated following the same pattern.

Describe alternative solutions you've considered

Alternatively, the author would need to do special handling for checkboxes to generate the link with the correct id format.

Introduce translatable capabilities to Hearth models

Is your feature request related to a problem?

Moving towards adding the resource library, we will need to implement translatable models. This implementation will need to involve several steps:

Describe the solution you'd like

Models can have translatable fields, which can be managed through translatable text input and textarea components.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Add two-factor authentication support (behind an install flag)

Is your feature request related to a problem?

Hearth should include support for Fortify's two-factor authentication capability.

Describe the solution you'd like

The hearth:install command should have a --two-factor flag which, if passed, will install the necessary controllers and views for two-factor authentication.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Input and select elements should use Laravel's new @checked and @selected Blade directives

Is your feature request related to a problem?

Currently, checkbox and radio button inputs use some gnarly template logic to determine whether the checked attribute should be set based on what's been retrieved from the database. Same with select elements for the selected attribute on selected options.

Describe the solution you'd like

We should adopt Laravel's new @checked and @selected blade directives.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Not applicable.

Improve multilingual features

  • Interactive command line interface to allow a user to enable support for additional languages when installing Hearth
  • Ensure that all language configuration data is loaded from config/locales.php

Upgrading an existing project's use of Hearth

This question occurs to me as I look at updating my testing work on eds-app - what would we need to do to make it easier to upgrade the use of Hearth in an existing project?

Right now, it seems the steps look like this, assuming you're updating from the main branch:

  1. Make sure you've versioned your existing work
  2. Run composer update
  3. Run artisan hearth:install
  4. Manually merge any changes to files that Hearth has replaced, such as the routes in web.php

Questions/Ideas:

  1. Can we extract the customizations Hearth needs to make and inject them at an appropriate place in files that are likely to be touched in a project using Hearth, rather than overwriting? (for web.php, something like what is described at https://5balloons.info/split-and-organise-laravel-routes-into-multiple-files/)

  2. Can we make the artisan hearth:install process more interactive, or make use of Git-style diffing?

These are not urgent, but worth thinking about due to experiences of drift / challenging upgrades on past projects trying to establish a common building base.

Button type is not properly overridden

Describe the bug

Supplying a type to the Button component does not override the default type (submit).

To reproduce

Steps to reproduce the behavior:

  1. Use the Button component with type="button".
  2. Inspect the rendered view.
  3. See that the button has type="submit".

Expected behavior

The button should have type="button".

Screenshots

Not applicable.

Technical details

Not applicable.

Additional context or notes

https://laravel.com/docs/9.x/blade#non-class-attribute-merging

Add checkbox and checkboxes components

Is your feature request related to a problem?

Like radio buttons (#35, #72), checkboxes need to be handled with their own component as you can't use @if within the markup of a Blade component and the checked attribute needs a conditional to determine whether or not it should be present.

Describe the solution you'd like

Create an <x-hearth-checkbox> component for single checkboxes and an <x-hearth-checkboxes> component for groups of related checkboxes.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Not applicable.

Vite support

Is your feature request related to a problem?

Following #157, Hearth should add support for Laravel Vite.

Describe the solution you'd like

See above.

Describe alternative solutions you've considered

We could add an interactive prompt to let users choose between Mix and Vite.

Additional context or notes

Not applicable.

Integration tests fail with Laravel 9.19

Describe the bug

Laravel >= 9.19 uses Vite for asset building instead of Laravel Mix. I think this is causing the integration tests to fail.

To reproduce

See: https://github.com/fluid-project/hearth/runs/7226153082?check_suite_focus=true

Expected behavior

Integration tests should pass.

Screenshots

Not applicable.

Technical details

Not applicable.

Additional context or notes

This could be resolved by using a slightly older version of Laravel in the interim but is probably better resolved by updating Hearth to support Vite.

Support individual hints for checkboxes and radio buttons

Is your feature request related to a problem?

Currently, the Checkboxes and Radio Buttons components support a single shared hint. In some circumstances, a unique hint may be required for each choice.

Describe the solution you'd like

The options parameter can be either a simple keyed array where the key is the option name and the value is the option label, e.g.:

$options = [
	'vanilla' => __('Vanilla'),
    'chocolate' => __('Chocolate'),
];

Or it can be a nested array where each key (name) points to an array with a label and a hint:

$options = [
	'vanilla' => [
        'label' => __('Vanilla'),
        'hint' => __('A timeless classic.'),
    ],
    'chocolate' => [
        'label' => __('Chocolate'),
        'hint' => __('A decadent treat.'),
    ],
];

If hints are supplied, the aria-describedby attribute of each checkbox or radio button will reference the unique hint (and, if supplied, a global hint).

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

See: https://www.figma.com/file/35NiapHpSyGcZP24H2HWZF/Ideation-Co-design-wires?node-id=236%3A10537

Add the ability for users to add resources to their collections

Is your feature request related to a problem?

With addition to the ResourceCollection model, we would also like to give ability to users to add resources to their collections.

Describe the solution you'd like

Update relationship between User and ResrouceCollection models to give such ability.

Additional context or notes

Would make sense to wait for the PR 143 to get merged first, before starting this work.

Improve organization invitation flow error handling

We should display some feedback (in a secure way that doesn't leak information) when following an organization invite email link fails.

One scenario identified so far:

- [email protected] is invited to join an organization
- [email protected] logs into the Hearth site and clicks the invitation email link sent to [email protected]
- what feedback should [email protected] see from the site?

A non-adversarial version of the scenario is someone has an account on the site under one email address, and is invited on a second email also under their control.

Empty locale added when trying to escape out of adding additional languages

Describe the bug

When trying to blackout of adding an additional language, added an empty locale instead.

To reproduce

Steps to reproduce the behavior:

  1. Setup a new Hearth installation
  2. after running php artisan hearth:install or sail artisan hearth:install choose to add more languages
  3. Instead of typing in a name for a language just hit the Enter key.
  4. At this point an empty string is added to the "locales" array.

Expected behavior

It should not be possible to add an invalid locale. Additionally there should be a way to escape out of adding additional locales without having to exit the entire install process.

Standardize options format for checkboxes, radio buttons and selects

Is your feature request related to a problem?

Right now we accept two option formats for checkboxes or radio buttons:

// Simple format

$options = [
	'value' => 'label',
];

// Complex format

$options = [
	'value' => [
		'label' => 'label',
		'hint' => 'hint', // Optional
	],
];

If options are passed to the component in the simple format, the options array is modified to conform to the complex format with a label key:

$options = array_map(function ($option) {
if (! is_array($option)) {
return ['label' => $option];
}
return $option;
}, $options);

Describe the solution you'd like

It would be more consistent to only accept one format. It would also be helpful to align the accepted format to that supplied by the new spatie/laravel-options package, which is as follows:

$options = [
	[
		'value' => 'value',
		'label' => 'label',
		'hint' => 'hint', // Optional
	],
];

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

See: https://freek.dev/2287-introducing-our-new-laravel-options-package

Use Pint instead of PHP CS Fixer

Is your feature request related to a problem?

Laravel now has it's own first-party code style tool, Laravel Pint. Hearth should use this.

Describe the solution you'd like

Replace PHP CS Fixer with Pint.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

Not applicable.

Client-side date input validation should only be triggered if all fields are filled in

Describe the bug

Currently client-side validation of the date input kicks in if someone has started filling it out (e.g. has entered a year and shifted focus to the month select). This is annoying! We should only validate the input if all fields have been filled in.

To reproduce

  1. Enter a year and shift focus to the month select with keyboard or mouse.
  2. See that the input shifts to an error state and the error message is revealed.

Expected behavior

No client-side validation should be triggered unless year, month, and day have been entered.

Component: single radio button

Is your feature request related to a problem?

In some circumstances (for example, when conditionally displayed form fields need to be inserted between radio buttons) it can be helpful to use a group of individual radio buttons in lieue of a RadioButtons group.

Describe the solution you'd like

A standalone RadioButton component with support for @checked.

Describe alternative solutions you've considered

Not applicable.

Additional context or notes

See also #144.

`hasErrors` doesn't catch errors for fields with array names

Describe the bug

The HandlesValidation trait's hasErrors method tries to look up errors based on a field's name. However, a field with an array name (for example, name="links[0][url]") will always return false as Laravel stores errors with dot notation (the errors would be found by looking up links.0.url).

To reproduce

Steps to reproduce the behavior:

  1. Use the <x-hearth-input> component with an array-based name.
  2. Produce a validation error.

Expected behavior

aria-invalid is added to the rendered component and a reference to the linked error is added to the aria-describedby attribute of the rendered component.

Screenshots

Not applicable.

Technical details

Not applicable.

Additional context or notes

Not applicable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.